@indexnetwork/protocol 6.5.0-rc.384.1 → 6.6.0-rc.386.1

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 (31) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/chat/chat.agent.d.ts.map +1 -1
  3. package/dist/chat/chat.agent.js +1 -0
  4. package/dist/chat/chat.agent.js.map +1 -1
  5. package/dist/chat/negotiator.prompt.d.ts.map +1 -1
  6. package/dist/chat/negotiator.prompt.js +2 -1
  7. package/dist/chat/negotiator.prompt.js.map +1 -1
  8. package/dist/chat/reporter.action.tools.d.ts +52 -0
  9. package/dist/chat/reporter.action.tools.d.ts.map +1 -0
  10. package/dist/chat/reporter.action.tools.js +146 -0
  11. package/dist/chat/reporter.action.tools.js.map +1 -0
  12. package/dist/chat/reporter.persona.d.ts.map +1 -1
  13. package/dist/chat/reporter.persona.js +5 -1
  14. package/dist/chat/reporter.persona.js.map +1 -1
  15. package/dist/chat/reporter.prompt.d.ts.map +1 -1
  16. package/dist/chat/reporter.prompt.js +18 -3
  17. package/dist/chat/reporter.prompt.js.map +1 -1
  18. package/dist/negotiation/negotiation.tools.d.ts.map +1 -1
  19. package/dist/negotiation/negotiation.tools.js +27 -1
  20. package/dist/negotiation/negotiation.tools.js.map +1 -1
  21. package/dist/shared/agent/tool.factory.d.ts.map +1 -1
  22. package/dist/shared/agent/tool.factory.js +1 -0
  23. package/dist/shared/agent/tool.factory.js.map +1 -1
  24. package/dist/shared/agent/tool.helpers.d.ts +8 -0
  25. package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
  26. package/dist/shared/agent/tool.helpers.js +2 -1
  27. package/dist/shared/agent/tool.helpers.js.map +1 -1
  28. package/dist/shared/interfaces/database.interface.d.ts +6 -0
  29. package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
  30. package/dist/shared/interfaces/database.interface.js.map +1 -1
  31. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"negotiator.prompt.js","sourceRoot":"/","sources":["chat/negotiator.prompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,iCAAiC,EAA8B,MAAM,sCAAsC,CAAC;AAgDrH;;;;GAIG;AACH,SAAS,wBAAwB,CAAC,QAAgB,EAAE,KAAc;IAChE,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO;;4EAEmE,QAAQ,GAAG,SAAS;;;;qNAIqH,CAAC;AACtN,CAAC;AAED;;;;;GAKG;AACH,SAAS,yBAAyB;IAChC,OAAO;;;;;;sHAM6G,CAAC;AACvH,CAAC;AAED;;;;GAIG;AACH,SAAS,uBAAuB;IAC9B,OAAO;;;;;;2JAMkJ,CAAC;AAC5J,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,4BAA4B,CAC1C,GAAwB,EACxB,IAA6B,EAC7B,QAA2B;IAE3B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,GAAG,CAAC,WAAW;QACpC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC,MAAM,CAAC;IACX,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,EAAE;QACnD,CAAC,CAAC,KAAK,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI;QACvC,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,cAAc,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAC5C,6EAA6E;IAC7E,kEAAkE;IAClE,MAAM,mBAAmB,GAAG,cAAc;QACxC,CAAC,CAAC,wBAAwB,CAAC,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC;QAClE,CAAC,CAAC,yBAAyB,EAAE,CAAC;IAChC,MAAM,aAAa,GAAG,iCAAiC,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB;QAC7C,CAAC,CAAC,iQAAiQ;QACnQ,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,mBAAmB,GAAG,cAAc;QACxC,CAAC,CAAC,iRAAiR;QACnR,CAAC,CAAC,sMAAsM,CAAC;IAC3M,MAAM,eAAe,GAAG,cAAc;QACpC,CAAC,CAAC,4GAA4G;QAC9G,CAAC,CAAC,iGAAiG,CAAC;IACtG,MAAM,yBAAyB,GAAG,cAAc;QAC9C,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,+EAA+E,CAAC;IAEpF,OAAO,WAAW,IAAI,CAAC,SAAS,+CAA+C,GAAG,CAAC,QAAQ;EAC3F,eAAe;mCACkB,GAAG,CAAC,QAAQ;;;;;EAK7C,mBAAmB;;;;;;EAMnB,mBAAmB,GAAG,aAAa,GAAG,kBAAkB;;0MAEgJ,eAAe;;;;;YAK7M,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,SAAS,UAAU,GAAG,CAAC,MAAM;;;;EAI5D,WAAW;;;;;EAKX,cAAc;;;;;;;;;kHASkG,yBAAyB;;;;;;;;;;;;;;;6GAe9B,eAAe;;;;;;;;;;8MAUkF,CAAC;AAC/M,CAAC","sourcesContent":["import type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\nimport { focusedIntentId } from \"../shared/agent/tool.scope.js\";\nimport { renderNegotiatorChatMemorySection, type NegotiatorMemoryEntry } from \"../negotiation/negotiation.memory.js\";\nimport type { IterationContext } from \"./chat.prompt.modules.js\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// NEGOTIATOR PERSONA SYSTEM PROMPT (P4.1)\n// ═══════════════════════════════════════════════════════════════════════════════\n//\n// The direct chat line between a user and their personal negotiator agent\n// (the `type='personal'` agent row). Unlike the orchestrator prompt, this\n// persona works for exactly one client: it reports on the client's\n// negotiations and opportunities, explains decisions from the negotiation\n// record, and acts only on explicit client instruction.\n//\n// P4.5 (IND-413): the negotiator also manages the client's signals, profile\n// knowledge, premises, community memberships, and contacts — discovery is\n// purely signal-based, so shaping signals here IS how the client steers\n// matching. It still has no direct discovery capability: matching runs in\n// the background from the signals.\n\n/** Identity options resolved from the user's personal negotiator agent row. */\nexport interface NegotiatorPromptOptions {\n /** The negotiator agent's display name (e.g. \"Ada's Negotiator\"). */\n agentName: string;\n /** The negotiator agent's description, when set on the agent row. */\n agentDescription?: string;\n /**\n * Human-readable label for the pinned signal when the session is\n * intent-scoped (P4.2/IND-403). The pin itself comes from the resolved\n * context's scope envelope; this label just saves a tool round-trip for\n * naming it. Ignored when the session has no intent scope.\n */\n pinnedIntentLabel?: string;\n /**\n * The client's negotiator memories (P5.3 read path) — accumulated notes\n * from negotiations and prior chats, rendered as a prompt section. The\n * audience is the client themself, so entries are shared context, not\n * secrets. Absent/empty → the prompt is byte-identical to before.\n */\n memory?: NegotiatorMemoryEntry[];\n /**\n * True when the `remember`/`forget` memory tools are registered for this\n * session (P5.4 — the composition root injects them only while negotiator\n * memory writes are enabled). Adds the memory-tool guidance section and\n * tool-reference rows; false/absent → prompt unchanged.\n */\n memoryToolsEnabled?: boolean;\n}\n\n/**\n * Renders the pinned-signal section for intent-scoped sessions (P4.2).\n * Awareness, not a sandbox: the conversation orbits this signal, but the\n * negotiator may still reference the client's other knowledge.\n */\nfunction buildPinnedSignalSection(intentId: string, label?: string): string {\n const labelLine = label?.trim() ? ` — “${label.trim()}”` : \"\";\n return `\n## Pinned signal\nThis conversation was opened from one of the client's signals (intent id: ${intentId}${labelLine}). Treat it as the working focus of this chat:\n- Open questions listed by read_pending_questions here are this signal's open questions — surface them early and work through them conversationally. The client answers them via the question cards shown in this chat, or conversationally: when they give you an explicit answer, record it with answer_pending_question.\n- Matches for this signal are already visible in the adjacent Radar. Do not repeat them or bulk-list them in chat. When the client explicitly references a match, use its opportunity and negotiation records to explain it or act on it; update_opportunity remains available only for their explicit instruction.\n- When the client restates or sharpens what they want here, propose an update to this signal (update_intent) or a new premise — on their confirmation — so background matching reflects it.\n- This is a focus, not a wall: you may still read the client's profile, premises, and other signals when the conversation needs the fuller picture, and general questions about their negotiations remain fair game.`;\n}\n\n/**\n * Renders the question-inbox section for the unscoped DM (P4.3/IND-404).\n * The DM is the client's primary surface for the system's open questions:\n * signal refinements, negotiation follow-ups, profile gaps. Cards render in\n * the chat; conversational answers are recorded via answer_pending_question.\n */\nfunction buildQuestionInboxSection(): string {\n return `\n## Client question inbox\nThe system collects open questions for your client (signal refinements, negotiation follow-ups, profile gaps), and this chat is the primary place they get handled. Question cards also render directly in this chat.\n- Early in a conversation — or whenever the client asks what needs their attention — call read_pending_questions and surface what is open, briefly and in plain language.\n- When the client asks why something is being asked, explain the question's context from the record it came from (the negotiation, opportunity, or signal behind it) — look it up, don't guess.\n- When the client answers a question conversationally, record it with answer_pending_question — pass exactly what they said (their chosen options and/or their own words), never an answer you inferred. If the tool reports the question was already answered or dismissed, tell them instead of retrying.\n- Never pressure the client to answer; the questions keep for later and remain available in the app's Questions page.`;\n}\n\n/**\n * Renders the remember/forget guidance section (P5.4). Only rendered when the\n * memory tools are actually registered, so the model is never told about\n * capabilities it does not have.\n */\nfunction buildMemoryToolsSection(): string {\n return `\n## Remembering and forgetting\nYou keep a private working memory for negotiations: standing disclosure rules, thresholds, and playbook notes. The client can inspect, edit, and delete everything you remember on their agent page (Memory tab).\n- When the client states a standing rule that should outlive this conversation — especially about what to disclose or protect (\"never share my budget\", \"don't reveal who my current clients are\") — save it with remember. Pick the kind honestly: disclosure_rule for sharing constraints, threshold for hard limits, playbook for tactics. Confirm in one short sentence what you saved and that they can review it on your agent page.\n- Only remember what the client actually said. Never speculate a rule into memory, and don't save one-off instructions that only apply to the current request.\n- When the client asks you to forget or retract something, use forget with their description of it. If several memories match, lay out the candidates in plain language and ask which one they mean.\n- Disclosure rules are standing consent: treat a remembered disclosure rule as binding in every future negotiation until the client changes or deletes it.`;\n}\n\n/**\n * Builds the system prompt for the negotiator chat persona.\n *\n * Grounded in the client's preloaded user/profile context; everything else\n * (negotiations, opportunities, signals, premises) must be fetched through\n * the client-scoped toolset each turn.\n *\n * @param ctx - Resolved tool context for the current session\n * @param opts - Identity from the client's personal negotiator agent row\n * @param _iterCtx - Iteration context (unused — the negotiator prompt has no\n * dynamic modules; the nudge is injected by the agent loop)\n * @returns The complete system prompt string\n */\nexport function buildNegotiatorSystemContent(\n ctx: ResolvedToolContext,\n opts: NegotiatorPromptOptions,\n _iterCtx?: IterationContext,\n): string {\n const userContext = JSON.stringify(ctx.user, null, 2);\n const profileContext = ctx.userProfile\n ? JSON.stringify(ctx.userProfile, null, 2)\n : \"null\";\n const descriptionLine = opts.agentDescription?.trim()\n ? `\\n${opts.agentDescription.trim()}\\n`\n : \"\";\n const pinnedIntentId = focusedIntentId(ctx);\n // Pinned sessions clamp the question tools to the signal and carry their own\n // question guidance; the unscoped DM gets the full inbox instead.\n const pinnedSignalSection = pinnedIntentId\n ? buildPinnedSignalSection(pinnedIntentId, opts.pinnedIntentLabel)\n : buildQuestionInboxSection();\n const memorySection = renderNegotiatorChatMemorySection(opts.memory ?? []);\n const memoryToolsSection = opts.memoryToolsEnabled ? buildMemoryToolsSection() : \"\";\n const memoryToolsRows = opts.memoryToolsEnabled\n ? `\\n| **remember** | kind, content | Save a standing rule the client just stated into your private negotiator memory — ONLY what they actually said |\\n| **forget** | memoryId? / description? | Delete a remembered rule when the client asks you to forget it |`\n : \"\";\n const opportunityGuidance = pinnedIntentId\n ? \"- **Discuss referenced opportunities**: matches for this signal are already visible in the adjacent Radar. Do not repeat or bulk-list them in chat. Explain or update an opportunity only when the client explicitly references it, and act only on their explicit instruction.\"\n : \"- **Review and act on opportunities**: show the client the opportunities currently waiting on them and what accepting or passing would mean; accept or pass on one only when they explicitly say so.\";\n const matchVisibility = pinnedIntentId\n ? \"New matches for this pinned signal appear in the adjacent Radar rather than as a repeated listing in chat.\"\n : \"New matches appear on the client's home page and can be reviewed in this chat as opportunities.\";\n const opportunityListingToolRow = pinnedIntentId\n ? \"\"\n : \"\\n| **list_opportunities** | — | List the client's actionable opportunities |\";\n\n return `You are ${opts.agentName}, the personal negotiator agent working for ${ctx.userName}.\n${descriptionLine}\nYou work for exactly one client: ${ctx.userName}. You represent them in negotiations with other members' agents across the network, and this chat is your direct line to them. Your job here is to keep your client informed about what you have been doing on their behalf, explain your reasoning honestly, and act only on their explicit instructions.\n\n## What you do in this chat\n- **Report on negotiations**: when the client asks what is happening, look up their negotiations and summarize status, counterparties, and where things stand.\n- **Explain decisions**: when the client asks why something was pursued, declined, or stalled (\"why did you pass on X?\"), find the relevant negotiation and answer from the actual record — the messages, outcomes, and reasoning stored there. Never reconstruct a rationale from memory.\n${opportunityGuidance}\n- **Manage their signals**: their active intents (signals) define what you negotiate for — and matching is driven entirely by them. When the client tells you what they are looking for, draft a clear, specific signal and create it; refine or retire signals when they ask. If a signal request is vague, read their profile and existing signals first, then propose a sharper wording before creating it. If they paste a link describing what they want, read it first and synthesize the signal from its content.\n- **Keep their knowledge current**: when the client shares a new fact about themselves (\"I moved to Berlin\", \"I stopped consulting\"), update their profile context or premises so future negotiations reflect reality. Read before you write — update the existing entry instead of duplicating it.\n- **Handle memberships**: list the communities they belong to and join or leave communities when they ask.\n- **Manage their contacts**: look up, add, remove, or import contacts when they ask (when contact features are enabled).\n- **Act on instruction**: every write — a negotiation response, an opportunity decision, a signal, a profile or premise change, a membership change, a contact change — happens only when the client explicitly asks for it in this conversation. Never write anything the client did not just ask for.\n${pinnedSignalSection}${memorySection}${memoryToolsSection}\n## What you cannot do here\n- **No direct discovery.** You cannot run matching or search for people yourself. Matching happens automatically in the background from the client's signals — shaping the signals is how you steer it. ${matchVisibility}\n- **No community administration.** You can join or leave communities for the client, but you cannot create, rename, or delete communities — point them to the app for that.\n- You cannot push updates after this conversation ends. You only report when asked.\n\n## Session\n- Client: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}\n\n### Client (preloaded context)\n\\`\\`\\`json\n${userContext}\n\\`\\`\\`\n\n### Client Context (preloaded context)\n\\`\\`\\`json\n${profileContext}\n\\`\\`\\`\n\n## Tools Reference\n\n| Tool | Params | What it does |\n|------|--------|-------------|\n| **list_negotiations** | status?, limit? | List the client's negotiations |\n| **get_negotiation** | negotiationId | Full negotiation record: messages, outcome, reasoning |\n| **respond_to_negotiation** | negotiationId, ... | Act on a negotiation — ONLY on explicit client instruction |${opportunityListingToolRow}\n| **read_pending_questions** | limit? | The system's open questions for the client (clamped to the pinned signal when one is set) |\n| **answer_pending_question** | questionId, selectedOptions?, freeText? | Record the client's explicit answer to a pending question — ONLY with an answer they actually gave |\n| **update_opportunity** | opportunityId, status | Accept/pass an opportunity — ONLY on explicit client instruction |\n| **read_intents** / **search_intents** | — / query | The client's active signals (what they're looking for) |\n| **create_intent** | description, networkId? | Draft a new signal — returns a proposal card the client approves in the UI |\n| **update_intent** / **delete_intent** | intentId, ... | Refine or retire a signal on instruction |\n| **read_intent_indexes** / **create_intent_index** / **delete_intent_index** | intentId, networkId | Where a signal is placed across communities |\n| **read_user_contexts** / **create_user_context** / **update_user_context** | ... | The client's profile knowledge — read before writing |\n| **preview_user_context** / **confirm_user_context** | ... | Preview/confirm profile updates from sources |\n| **read_premises** / **create_premise** / **update_premise** / **retract_premise** | ... | The client's premises (facts they've established) |\n| **read_networks** / **read_network_memberships** | — | The client's communities and memberships |\n| **create_network_membership** / **delete_network_membership** | networkId | Join/leave a community on instruction |\n| **list_contacts** / **search_contacts** / **add_contact** / **remove_contact** | ... | The client's contacts |\n| **import_contacts** / **import_gmail_contacts** | ... | Bulk contact import on instruction |\n| **scrape_url** | url, objective | Read a link the client pasted (e.g. before drafting a signal from it) |${memoryToolsRows}\n\n## Grounding rules\n- **Never fabricate.** Every claim about a negotiation, opportunity, signal, or premise must come from a tool result in this conversation. If you have not looked it up this turn, look it up before answering. Only the client's identity and profile above are preloaded.\n- **Check tool results before confirming.** Never claim an action succeeded without a successful tool result for it.\n- **Be honest about your own actions.** If the record shows you made a judgment call the client disagrees with, explain the reasoning from the record — do not get defensive, and do not invent justifications the record does not support.\n- **Pass proposal cards through verbatim.** When a tool result contains a fenced code block meant for the app (e.g. \\`\\`\\`intent_proposal from create_intent), include that block verbatim in your reply — the app renders it as an interactive card the client approves or skips. Never write such a block yourself without a backing tool result.\n- **Never expose IDs, UUIDs, tool names, or raw JSON** to the client. Translate everything into natural language; refer to people and opportunities by name. (Fenced proposal blocks from tool results are the one exception — they are rendered as cards, not shown as JSON.)\n- **Respond in the language of the client's latest message.**\n- **Voice**: first person, loyal but candid, calm and concise. No hype, no networking clichés, no exaggeration. You are their agent, not a salesperson.\n- When calling tools, first write a short natural sentence plus a \\`>\\` blockquote describing what you are checking (e.g. \"> Checking the record with Alice\"), then leave an empty line after the blockquote.`;\n}\n"]}
1
+ {"version":3,"file":"negotiator.prompt.js","sourceRoot":"/","sources":["chat/negotiator.prompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,iCAAiC,EAA8B,MAAM,sCAAsC,CAAC;AAgDrH;;;;GAIG;AACH,SAAS,wBAAwB,CAAC,QAAgB,EAAE,KAAc;IAChE,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO;;4EAEmE,QAAQ,GAAG,SAAS;;;;;qNAKqH,CAAC;AACtN,CAAC;AAED;;;;;GAKG;AACH,SAAS,yBAAyB;IAChC,OAAO;;;;;;sHAM6G,CAAC;AACvH,CAAC;AAED;;;;GAIG;AACH,SAAS,uBAAuB;IAC9B,OAAO;;;;;;2JAMkJ,CAAC;AAC5J,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,4BAA4B,CAC1C,GAAwB,EACxB,IAA6B,EAC7B,QAA2B;IAE3B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,GAAG,CAAC,WAAW;QACpC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC,MAAM,CAAC;IACX,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,EAAE;QACnD,CAAC,CAAC,KAAK,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI;QACvC,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,cAAc,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAC5C,6EAA6E;IAC7E,kEAAkE;IAClE,MAAM,mBAAmB,GAAG,cAAc;QACxC,CAAC,CAAC,wBAAwB,CAAC,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC;QAClE,CAAC,CAAC,yBAAyB,EAAE,CAAC;IAChC,MAAM,aAAa,GAAG,iCAAiC,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB;QAC7C,CAAC,CAAC,iQAAiQ;QACnQ,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,mBAAmB,GAAG,cAAc;QACxC,CAAC,CAAC,iRAAiR;QACnR,CAAC,CAAC,sMAAsM,CAAC;IAC3M,MAAM,eAAe,GAAG,cAAc;QACpC,CAAC,CAAC,4GAA4G;QAC9G,CAAC,CAAC,iGAAiG,CAAC;IACtG,MAAM,yBAAyB,GAAG,cAAc;QAC9C,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,+EAA+E,CAAC;IAEpF,OAAO,WAAW,IAAI,CAAC,SAAS,+CAA+C,GAAG,CAAC,QAAQ;EAC3F,eAAe;mCACkB,GAAG,CAAC,QAAQ;;;;;EAK7C,mBAAmB;;;;;;EAMnB,mBAAmB,GAAG,aAAa,GAAG,kBAAkB;;0MAEgJ,eAAe;;;;;YAK7M,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,SAAS,UAAU,GAAG,CAAC,MAAM;;;;EAI5D,WAAW;;;;;EAKX,cAAc;;;;;;;;;kHASkG,yBAAyB;;;;;;;;;;;;;;;6GAe9B,eAAe;;;;;;;;;;8MAUkF,CAAC;AAC/M,CAAC","sourcesContent":["import type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\nimport { focusedIntentId } from \"../shared/agent/tool.scope.js\";\nimport { renderNegotiatorChatMemorySection, type NegotiatorMemoryEntry } from \"../negotiation/negotiation.memory.js\";\nimport type { IterationContext } from \"./chat.prompt.modules.js\";\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// NEGOTIATOR PERSONA SYSTEM PROMPT (P4.1)\n// ═══════════════════════════════════════════════════════════════════════════════\n//\n// The direct chat line between a user and their personal negotiator agent\n// (the `type='personal'` agent row). Unlike the orchestrator prompt, this\n// persona works for exactly one client: it reports on the client's\n// negotiations and opportunities, explains decisions from the negotiation\n// record, and acts only on explicit client instruction.\n//\n// P4.5 (IND-413): the negotiator also manages the client's signals, profile\n// knowledge, premises, community memberships, and contacts — discovery is\n// purely signal-based, so shaping signals here IS how the client steers\n// matching. It still has no direct discovery capability: matching runs in\n// the background from the signals.\n\n/** Identity options resolved from the user's personal negotiator agent row. */\nexport interface NegotiatorPromptOptions {\n /** The negotiator agent's display name (e.g. \"Ada's Negotiator\"). */\n agentName: string;\n /** The negotiator agent's description, when set on the agent row. */\n agentDescription?: string;\n /**\n * Human-readable label for the pinned signal when the session is\n * intent-scoped (P4.2/IND-403). The pin itself comes from the resolved\n * context's scope envelope; this label just saves a tool round-trip for\n * naming it. Ignored when the session has no intent scope.\n */\n pinnedIntentLabel?: string;\n /**\n * The client's negotiator memories (P5.3 read path) — accumulated notes\n * from negotiations and prior chats, rendered as a prompt section. The\n * audience is the client themself, so entries are shared context, not\n * secrets. Absent/empty → the prompt is byte-identical to before.\n */\n memory?: NegotiatorMemoryEntry[];\n /**\n * True when the `remember`/`forget` memory tools are registered for this\n * session (P5.4 — the composition root injects them only while negotiator\n * memory writes are enabled). Adds the memory-tool guidance section and\n * tool-reference rows; false/absent → prompt unchanged.\n */\n memoryToolsEnabled?: boolean;\n}\n\n/**\n * Renders the pinned-signal section for intent-scoped sessions (P4.2).\n * Awareness, not a sandbox: the conversation orbits this signal, but the\n * negotiator may still reference the client's other knowledge.\n */\nfunction buildPinnedSignalSection(intentId: string, label?: string): string {\n const labelLine = label?.trim() ? ` — “${label.trim()}”` : \"\";\n return `\n## Pinned signal\nThis conversation was opened from one of the client's signals (intent id: ${intentId}${labelLine}). Treat it as the working focus of this chat:\n- Open questions listed by read_pending_questions here are this signal's open questions — surface them early and work through them conversationally. The client answers them via the question cards shown in this chat, or conversationally: when they give you an explicit answer, record it with answer_pending_question.\n- Matches for this signal are already visible in the adjacent Radar. Do not repeat them or bulk-list them in chat. When the client explicitly references a match, use its opportunity and negotiation records to explain it or act on it; update_opportunity remains available only for their explicit instruction.\n- When summarizing negotiations, always state the scope: say “for this signal” for the default pinned view, or “across all your signals” when the client explicitly asks for full history. Separate CURRENT items (active or waiting on you) from COMPLETED history. If there are zero current items, say that plainly before mentioning any completed history. Never imply an ongoing negotiation that the same-turn list_negotiations result does not show.\n- When the client restates or sharpens what they want here, propose an update to this signal (update_intent) or a new premise — on their confirmation — so background matching reflects it.\n- This is a focus, not a wall: you may still read the client's profile, premises, and other signals when the conversation needs the fuller picture, and general questions about their negotiations remain fair game.`;\n}\n\n/**\n * Renders the question-inbox section for the unscoped DM (P4.3/IND-404).\n * The DM is the client's primary surface for the system's open questions:\n * signal refinements, negotiation follow-ups, profile gaps. Cards render in\n * the chat; conversational answers are recorded via answer_pending_question.\n */\nfunction buildQuestionInboxSection(): string {\n return `\n## Client question inbox\nThe system collects open questions for your client (signal refinements, negotiation follow-ups, profile gaps), and this chat is the primary place they get handled. Question cards also render directly in this chat.\n- Early in a conversation — or whenever the client asks what needs their attention — call read_pending_questions and surface what is open, briefly and in plain language.\n- When the client asks why something is being asked, explain the question's context from the record it came from (the negotiation, opportunity, or signal behind it) — look it up, don't guess.\n- When the client answers a question conversationally, record it with answer_pending_question — pass exactly what they said (their chosen options and/or their own words), never an answer you inferred. If the tool reports the question was already answered or dismissed, tell them instead of retrying.\n- Never pressure the client to answer; the questions keep for later and remain available in the app's Questions page.`;\n}\n\n/**\n * Renders the remember/forget guidance section (P5.4). Only rendered when the\n * memory tools are actually registered, so the model is never told about\n * capabilities it does not have.\n */\nfunction buildMemoryToolsSection(): string {\n return `\n## Remembering and forgetting\nYou keep a private working memory for negotiations: standing disclosure rules, thresholds, and playbook notes. The client can inspect, edit, and delete everything you remember on their agent page (Memory tab).\n- When the client states a standing rule that should outlive this conversation — especially about what to disclose or protect (\"never share my budget\", \"don't reveal who my current clients are\") — save it with remember. Pick the kind honestly: disclosure_rule for sharing constraints, threshold for hard limits, playbook for tactics. Confirm in one short sentence what you saved and that they can review it on your agent page.\n- Only remember what the client actually said. Never speculate a rule into memory, and don't save one-off instructions that only apply to the current request.\n- When the client asks you to forget or retract something, use forget with their description of it. If several memories match, lay out the candidates in plain language and ask which one they mean.\n- Disclosure rules are standing consent: treat a remembered disclosure rule as binding in every future negotiation until the client changes or deletes it.`;\n}\n\n/**\n * Builds the system prompt for the negotiator chat persona.\n *\n * Grounded in the client's preloaded user/profile context; everything else\n * (negotiations, opportunities, signals, premises) must be fetched through\n * the client-scoped toolset each turn.\n *\n * @param ctx - Resolved tool context for the current session\n * @param opts - Identity from the client's personal negotiator agent row\n * @param _iterCtx - Iteration context (unused — the negotiator prompt has no\n * dynamic modules; the nudge is injected by the agent loop)\n * @returns The complete system prompt string\n */\nexport function buildNegotiatorSystemContent(\n ctx: ResolvedToolContext,\n opts: NegotiatorPromptOptions,\n _iterCtx?: IterationContext,\n): string {\n const userContext = JSON.stringify(ctx.user, null, 2);\n const profileContext = ctx.userProfile\n ? JSON.stringify(ctx.userProfile, null, 2)\n : \"null\";\n const descriptionLine = opts.agentDescription?.trim()\n ? `\\n${opts.agentDescription.trim()}\\n`\n : \"\";\n const pinnedIntentId = focusedIntentId(ctx);\n // Pinned sessions clamp the question tools to the signal and carry their own\n // question guidance; the unscoped DM gets the full inbox instead.\n const pinnedSignalSection = pinnedIntentId\n ? buildPinnedSignalSection(pinnedIntentId, opts.pinnedIntentLabel)\n : buildQuestionInboxSection();\n const memorySection = renderNegotiatorChatMemorySection(opts.memory ?? []);\n const memoryToolsSection = opts.memoryToolsEnabled ? buildMemoryToolsSection() : \"\";\n const memoryToolsRows = opts.memoryToolsEnabled\n ? `\\n| **remember** | kind, content | Save a standing rule the client just stated into your private negotiator memory — ONLY what they actually said |\\n| **forget** | memoryId? / description? | Delete a remembered rule when the client asks you to forget it |`\n : \"\";\n const opportunityGuidance = pinnedIntentId\n ? \"- **Discuss referenced opportunities**: matches for this signal are already visible in the adjacent Radar. Do not repeat or bulk-list them in chat. Explain or update an opportunity only when the client explicitly references it, and act only on their explicit instruction.\"\n : \"- **Review and act on opportunities**: show the client the opportunities currently waiting on them and what accepting or passing would mean; accept or pass on one only when they explicitly say so.\";\n const matchVisibility = pinnedIntentId\n ? \"New matches for this pinned signal appear in the adjacent Radar rather than as a repeated listing in chat.\"\n : \"New matches appear on the client's home page and can be reviewed in this chat as opportunities.\";\n const opportunityListingToolRow = pinnedIntentId\n ? \"\"\n : \"\\n| **list_opportunities** | — | List the client's actionable opportunities |\";\n\n return `You are ${opts.agentName}, the personal negotiator agent working for ${ctx.userName}.\n${descriptionLine}\nYou work for exactly one client: ${ctx.userName}. You represent them in negotiations with other members' agents across the network, and this chat is your direct line to them. Your job here is to keep your client informed about what you have been doing on their behalf, explain your reasoning honestly, and act only on their explicit instructions.\n\n## What you do in this chat\n- **Report on negotiations**: when the client asks what is happening, look up their negotiations and summarize status, counterparties, and where things stand.\n- **Explain decisions**: when the client asks why something was pursued, declined, or stalled (\"why did you pass on X?\"), find the relevant negotiation and answer from the actual record — the messages, outcomes, and reasoning stored there. Never reconstruct a rationale from memory.\n${opportunityGuidance}\n- **Manage their signals**: their active intents (signals) define what you negotiate for — and matching is driven entirely by them. When the client tells you what they are looking for, draft a clear, specific signal and create it; refine or retire signals when they ask. If a signal request is vague, read their profile and existing signals first, then propose a sharper wording before creating it. If they paste a link describing what they want, read it first and synthesize the signal from its content.\n- **Keep their knowledge current**: when the client shares a new fact about themselves (\"I moved to Berlin\", \"I stopped consulting\"), update their profile context or premises so future negotiations reflect reality. Read before you write — update the existing entry instead of duplicating it.\n- **Handle memberships**: list the communities they belong to and join or leave communities when they ask.\n- **Manage their contacts**: look up, add, remove, or import contacts when they ask (when contact features are enabled).\n- **Act on instruction**: every write — a negotiation response, an opportunity decision, a signal, a profile or premise change, a membership change, a contact change — happens only when the client explicitly asks for it in this conversation. Never write anything the client did not just ask for.\n${pinnedSignalSection}${memorySection}${memoryToolsSection}\n## What you cannot do here\n- **No direct discovery.** You cannot run matching or search for people yourself. Matching happens automatically in the background from the client's signals — shaping the signals is how you steer it. ${matchVisibility}\n- **No community administration.** You can join or leave communities for the client, but you cannot create, rename, or delete communities — point them to the app for that.\n- You cannot push updates after this conversation ends. You only report when asked.\n\n## Session\n- Client: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}\n\n### Client (preloaded context)\n\\`\\`\\`json\n${userContext}\n\\`\\`\\`\n\n### Client Context (preloaded context)\n\\`\\`\\`json\n${profileContext}\n\\`\\`\\`\n\n## Tools Reference\n\n| Tool | Params | What it does |\n|------|--------|-------------|\n| **list_negotiations** | status?, scope?, limit? | List negotiations (clamped to the pinned signal when one is set; pass scope:'all' for full history) |\n| **get_negotiation** | negotiationId | Full negotiation record: messages, outcome, reasoning |\n| **respond_to_negotiation** | negotiationId, ... | Act on a negotiation — ONLY on explicit client instruction |${opportunityListingToolRow}\n| **read_pending_questions** | limit? | The system's open questions for the client (clamped to the pinned signal when one is set) |\n| **answer_pending_question** | questionId, selectedOptions?, freeText? | Record the client's explicit answer to a pending question — ONLY with an answer they actually gave |\n| **update_opportunity** | opportunityId, status | Accept/pass an opportunity — ONLY on explicit client instruction |\n| **read_intents** / **search_intents** | — / query | The client's active signals (what they're looking for) |\n| **create_intent** | description, networkId? | Draft a new signal — returns a proposal card the client approves in the UI |\n| **update_intent** / **delete_intent** | intentId, ... | Refine or retire a signal on instruction |\n| **read_intent_indexes** / **create_intent_index** / **delete_intent_index** | intentId, networkId | Where a signal is placed across communities |\n| **read_user_contexts** / **create_user_context** / **update_user_context** | ... | The client's profile knowledge — read before writing |\n| **preview_user_context** / **confirm_user_context** | ... | Preview/confirm profile updates from sources |\n| **read_premises** / **create_premise** / **update_premise** / **retract_premise** | ... | The client's premises (facts they've established) |\n| **read_networks** / **read_network_memberships** | — | The client's communities and memberships |\n| **create_network_membership** / **delete_network_membership** | networkId | Join/leave a community on instruction |\n| **list_contacts** / **search_contacts** / **add_contact** / **remove_contact** | ... | The client's contacts |\n| **import_contacts** / **import_gmail_contacts** | ... | Bulk contact import on instruction |\n| **scrape_url** | url, objective | Read a link the client pasted (e.g. before drafting a signal from it) |${memoryToolsRows}\n\n## Grounding rules\n- **Never fabricate.** Every claim about a negotiation, opportunity, signal, or premise must come from a tool result in this conversation. If you have not looked it up this turn, look it up before answering. Only the client's identity and profile above are preloaded.\n- **Check tool results before confirming.** Never claim an action succeeded without a successful tool result for it.\n- **Be honest about your own actions.** If the record shows you made a judgment call the client disagrees with, explain the reasoning from the record — do not get defensive, and do not invent justifications the record does not support.\n- **Pass proposal cards through verbatim.** When a tool result contains a fenced code block meant for the app (e.g. \\`\\`\\`intent_proposal from create_intent), include that block verbatim in your reply — the app renders it as an interactive card the client approves or skips. Never write such a block yourself without a backing tool result.\n- **Never expose IDs, UUIDs, tool names, or raw JSON** to the client. Translate everything into natural language; refer to people and opportunities by name. (Fenced proposal blocks from tool results are the one exception — they are rendered as cards, not shown as JSON.)\n- **Respond in the language of the client's latest message.**\n- **Voice**: first person, loyal but candid, calm and concise. No hype, no networking clichés, no exaggeration. You are their agent, not a salesperson.\n- When calling tools, first write a short natural sentence plus a \\`>\\` blockquote describing what you are checking (e.g. \"> Checking the record with Alice\"), then leave an empty line after the blockquote.`;\n}\n"]}
@@ -0,0 +1,52 @@
1
+ import type { ChatGraphCompositeDatabase, UserDatabase } from "../shared/interfaces/database.interface.js";
2
+ import type { ChatTools, ResolvedToolContext, ToolContext } from "../shared/agent/tool.factory.js";
3
+ export declare const AGENT_ACTION_PROPOSAL_FENCE = "agent_action_proposal";
4
+ export type AgentCleanupActionType = "retract_premise" | "narrow_signal" | "pause_signal";
5
+ export interface AgentActionProposalSnapshot {
6
+ status: string;
7
+ updatedAt?: string;
8
+ payload?: string;
9
+ summary?: string | null;
10
+ assertionText?: string;
11
+ }
12
+ interface AgentActionProposalActionBase {
13
+ entityId: string;
14
+ currentState: string;
15
+ proposedOperation: string;
16
+ evidence?: string;
17
+ skipped?: boolean;
18
+ reason?: string;
19
+ snapshot?: AgentActionProposalSnapshot;
20
+ description?: string;
21
+ }
22
+ export type AgentActionProposalAction = (AgentActionProposalActionBase & {
23
+ type: "retract_premise";
24
+ }) | (AgentActionProposalActionBase & {
25
+ type: "narrow_signal";
26
+ }) | (AgentActionProposalActionBase & {
27
+ type: "pause_signal";
28
+ });
29
+ export interface AgentActionProposal {
30
+ proposalId: string;
31
+ userId: string;
32
+ conversationId?: string;
33
+ actions: AgentActionProposalAction[];
34
+ }
35
+ /** Host persistence bridge for proposals; the reporter tool never mutates domain rows. */
36
+ export interface AgentActionProposalStore {
37
+ createProposal(proposal: AgentActionProposal): Promise<void>;
38
+ }
39
+ /**
40
+ * Creates the reporter-only proposal tool. It validates owner state and records
41
+ * snapshots, but intentionally has no mutation dependency.
42
+ */
43
+ export declare function createProposeCleanupActionsTool(input: {
44
+ database: ChatGraphCompositeDatabase;
45
+ userDb: UserDatabase;
46
+ context: ResolvedToolContext;
47
+ store: AgentActionProposalStore;
48
+ }): ChatTools[number];
49
+ /** Build the optional action tool from the host-provided ToolContext. */
50
+ export declare function createReporterActionTool(deps: ToolContext, context: ResolvedToolContext, userDb: UserDatabase): ChatTools[number] | null;
51
+ export {};
52
+ //# sourceMappingURL=reporter.action.tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.action.tools.d.ts","sourceRoot":"/","sources":["chat/reporter.action.tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC3G,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAGnG,eAAO,MAAM,2BAA2B,0BAA0B,CAAC;AAEnE,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,eAAe,GAAG,cAAc,CAAC;AAE1F,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,6BAA6B;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,2BAA2B,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,yBAAyB,GACjC,CAAC,6BAA6B,GAAG;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,CAAC,GAC7D,CAAC,6BAA6B,GAAG;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC,GAC3D,CAAC,6BAA6B,GAAG;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC,CAAC;AAE/D,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,yBAAyB,EAAE,CAAC;CACtC;AAED,0FAA0F;AAC1F,MAAM,WAAW,wBAAwB;IACvC,cAAc,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AA8CD;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE;IACrD,QAAQ,EAAE,0BAA0B,CAAC;IACrC,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,KAAK,EAAE,wBAAwB,CAAC;CACjC,GAAG,SAAS,CAAC,MAAM,CAAC,CAsGpB;AAED,yEAAyE;AACzE,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,YAAY,GACnB,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,CAQ1B"}
@@ -0,0 +1,146 @@
1
+ import { tool } from "@langchain/core/tools";
2
+ import { z } from "zod";
3
+ import { success } from "../shared/agent/tool.helpers.js";
4
+ export const AGENT_ACTION_PROPOSAL_FENCE = "agent_action_proposal";
5
+ const actionInputSchema = z.discriminatedUnion("type", [
6
+ z.object({
7
+ type: z.literal("retract_premise"),
8
+ premiseId: z.string().trim().min(1),
9
+ }).strict(),
10
+ z.object({
11
+ type: z.literal("narrow_signal"),
12
+ intentId: z.string().trim().min(1),
13
+ description: z.string().trim().min(1),
14
+ }).strict(),
15
+ z.object({
16
+ type: z.literal("pause_signal"),
17
+ intentId: z.string().trim().min(1),
18
+ evidence: z.string().trim().min(1, "pause_signal evidence is required"),
19
+ }).strict(),
20
+ ]);
21
+ const proposeCleanupActionsSchema = z.object({
22
+ actions: z.array(actionInputSchema).min(1).max(5),
23
+ }).strict();
24
+ function isFullUuid(value) {
25
+ return z.string().uuid().safeParse(value).success;
26
+ }
27
+ function skippedAction(action, entityId, proposedOperation, reason) {
28
+ return {
29
+ type: action.type,
30
+ entityId,
31
+ currentState: "UNKNOWN",
32
+ proposedOperation,
33
+ skipped: true,
34
+ reason,
35
+ ...(action.type === "pause_signal" ? { evidence: action.evidence } : {}),
36
+ };
37
+ }
38
+ /**
39
+ * Creates the reporter-only proposal tool. It validates owner state and records
40
+ * snapshots, but intentionally has no mutation dependency.
41
+ */
42
+ export function createProposeCleanupActionsTool(input) {
43
+ const { database, userDb, context, store } = input;
44
+ return tool(async (query) => {
45
+ const proposalId = crypto.randomUUID();
46
+ const actions = [];
47
+ for (const action of query.actions) {
48
+ const entityId = action.type === "retract_premise" ? action.premiseId : action.intentId;
49
+ const operation = action.type === "retract_premise"
50
+ ? "RETRACT_PREMISE"
51
+ : action.type === "narrow_signal" ? "NARROW_SIGNAL" : "PAUSE_SIGNAL";
52
+ if (!isFullUuid(entityId)) {
53
+ actions.push(skippedAction(action, entityId, operation, "A full UUID is required; suffixes and ambiguous IDs are not accepted."));
54
+ continue;
55
+ }
56
+ if (action.type === "retract_premise") {
57
+ const premise = await database.getPremise(entityId).catch(() => null);
58
+ if (!premise || premise.userId !== context.userId) {
59
+ actions.push(skippedAction(action, entityId, operation, "Premise not found or not owned by the authenticated user."));
60
+ continue;
61
+ }
62
+ if (premise.status !== "ACTIVE") {
63
+ actions.push({
64
+ type: action.type,
65
+ entityId,
66
+ currentState: premise.status,
67
+ proposedOperation: operation,
68
+ skipped: true,
69
+ reason: `Premise is already ${premise.status}.`,
70
+ });
71
+ continue;
72
+ }
73
+ actions.push({
74
+ type: action.type,
75
+ entityId,
76
+ currentState: premise.status,
77
+ proposedOperation: operation,
78
+ snapshot: { status: premise.status, updatedAt: premise.updatedAt.toISOString(), assertionText: premise.assertion.text },
79
+ });
80
+ continue;
81
+ }
82
+ const intent = await userDb.getIntent(entityId).catch(() => null);
83
+ if (!intent) {
84
+ actions.push(skippedAction(action, entityId, operation, "Signal not found or not owned by the authenticated user."));
85
+ continue;
86
+ }
87
+ const status = intent.status ?? "ACTIVE";
88
+ if (intent.archivedAt || status === "FULFILLED" || status === "EXPIRED") {
89
+ actions.push({
90
+ type: action.type,
91
+ entityId,
92
+ currentState: intent.archivedAt ? "ARCHIVED" : status,
93
+ proposedOperation: operation,
94
+ ...(action.type === "pause_signal" ? { evidence: action.evidence } : {}),
95
+ skipped: true,
96
+ reason: "Signal is archived or terminal and cannot be changed.",
97
+ });
98
+ continue;
99
+ }
100
+ actions.push({
101
+ type: action.type,
102
+ entityId,
103
+ currentState: status,
104
+ proposedOperation: operation,
105
+ ...(action.type === "pause_signal" ? { evidence: action.evidence } : {}),
106
+ ...(action.type === "narrow_signal" ? { description: action.description } : {}),
107
+ snapshot: {
108
+ status,
109
+ updatedAt: intent.updatedAt.toISOString(),
110
+ payload: intent.payload,
111
+ summary: intent.summary,
112
+ },
113
+ });
114
+ }
115
+ await store.createProposal({
116
+ proposalId,
117
+ userId: context.userId,
118
+ ...(context.sessionId ? { conversationId: context.sessionId } : {}),
119
+ actions,
120
+ });
121
+ const renderedActions = actions.map(({ snapshot: _snapshot, ...entry }) => entry);
122
+ const block = `\`\`\`${AGENT_ACTION_PROPOSAL_FENCE}\n${JSON.stringify({ proposalId, actions: renderedActions })}\n\`\`\``;
123
+ return success({
124
+ proposed: true,
125
+ proposalId,
126
+ actions: renderedActions,
127
+ message: `IMPORTANT: Include this \`\`\`agent_action_proposal code block EXACTLY as-is in your response. It is a REQUEST for owner confirmation; it performs no mutation until the owner confirms it in the UI:\n\n${block}`,
128
+ });
129
+ }, {
130
+ name: "propose_cleanup_actions",
131
+ description: "Prepare owner-confirmed cleanup actions from same-turn grounded reads. This tool never mutates data.",
132
+ schema: proposeCleanupActionsSchema,
133
+ });
134
+ }
135
+ /** Build the optional action tool from the host-provided ToolContext. */
136
+ export function createReporterActionTool(deps, context, userDb) {
137
+ if (!deps.actionToolsEnabled || !deps.actionProposalStore)
138
+ return null;
139
+ return createProposeCleanupActionsTool({
140
+ database: deps.database,
141
+ userDb,
142
+ context,
143
+ store: deps.actionProposalStore,
144
+ });
145
+ }
146
+ //# sourceMappingURL=reporter.action.tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.action.tools.js","sourceRoot":"/","sources":["chat/reporter.action.tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\n\nimport type { ChatGraphCompositeDatabase, UserDatabase } from \"../shared/interfaces/database.interface.js\";\nimport type { ChatTools, ResolvedToolContext, ToolContext } from \"../shared/agent/tool.factory.js\";\nimport { success } from \"../shared/agent/tool.helpers.js\";\n\nexport const AGENT_ACTION_PROPOSAL_FENCE = \"agent_action_proposal\";\n\nexport type AgentCleanupActionType = \"retract_premise\" | \"narrow_signal\" | \"pause_signal\";\n\nexport interface AgentActionProposalSnapshot {\n status: string;\n updatedAt?: string;\n payload?: string;\n summary?: string | null;\n assertionText?: string;\n}\n\ninterface AgentActionProposalActionBase {\n entityId: string;\n currentState: string;\n proposedOperation: string;\n evidence?: string;\n skipped?: boolean;\n reason?: string;\n snapshot?: AgentActionProposalSnapshot;\n description?: string;\n}\n\nexport type AgentActionProposalAction =\n | (AgentActionProposalActionBase & { type: \"retract_premise\" })\n | (AgentActionProposalActionBase & { type: \"narrow_signal\" })\n | (AgentActionProposalActionBase & { type: \"pause_signal\" });\n\nexport interface AgentActionProposal {\n proposalId: string;\n userId: string;\n conversationId?: string;\n actions: AgentActionProposalAction[];\n}\n\n/** Host persistence bridge for proposals; the reporter tool never mutates domain rows. */\nexport interface AgentActionProposalStore {\n createProposal(proposal: AgentActionProposal): Promise<void>;\n}\n\nconst actionInputSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"retract_premise\"),\n premiseId: z.string().trim().min(1),\n }).strict(),\n z.object({\n type: z.literal(\"narrow_signal\"),\n intentId: z.string().trim().min(1),\n description: z.string().trim().min(1),\n }).strict(),\n z.object({\n type: z.literal(\"pause_signal\"),\n intentId: z.string().trim().min(1),\n evidence: z.string().trim().min(1, \"pause_signal evidence is required\"),\n }).strict(),\n]);\n\nconst proposeCleanupActionsSchema = z.object({\n actions: z.array(actionInputSchema).min(1).max(5),\n}).strict();\n\ntype ActionInput = z.infer<typeof actionInputSchema>;\n\nfunction isFullUuid(value: string): boolean {\n return z.string().uuid().safeParse(value).success;\n}\n\nfunction skippedAction(\n action: ActionInput,\n entityId: string,\n proposedOperation: string,\n reason: string,\n): AgentActionProposalAction {\n return {\n type: action.type,\n entityId,\n currentState: \"UNKNOWN\",\n proposedOperation,\n skipped: true,\n reason,\n ...(action.type === \"pause_signal\" ? { evidence: action.evidence } : {}),\n };\n}\n\n/**\n * Creates the reporter-only proposal tool. It validates owner state and records\n * snapshots, but intentionally has no mutation dependency.\n */\nexport function createProposeCleanupActionsTool(input: {\n database: ChatGraphCompositeDatabase;\n userDb: UserDatabase;\n context: ResolvedToolContext;\n store: AgentActionProposalStore;\n}): ChatTools[number] {\n const { database, userDb, context, store } = input;\n\n return tool(\n async (query: { actions: ActionInput[] }) => {\n const proposalId = crypto.randomUUID();\n const actions: AgentActionProposalAction[] = [];\n\n for (const action of query.actions) {\n const entityId = action.type === \"retract_premise\" ? action.premiseId : action.intentId;\n const operation = action.type === \"retract_premise\"\n ? \"RETRACT_PREMISE\"\n : action.type === \"narrow_signal\" ? \"NARROW_SIGNAL\" : \"PAUSE_SIGNAL\";\n\n if (!isFullUuid(entityId)) {\n actions.push(skippedAction(action, entityId, operation, \"A full UUID is required; suffixes and ambiguous IDs are not accepted.\"));\n continue;\n }\n\n if (action.type === \"retract_premise\") {\n const premise = await database.getPremise(entityId).catch(() => null);\n if (!premise || premise.userId !== context.userId) {\n actions.push(skippedAction(action, entityId, operation, \"Premise not found or not owned by the authenticated user.\"));\n continue;\n }\n if (premise.status !== \"ACTIVE\") {\n actions.push({\n type: action.type,\n entityId,\n currentState: premise.status,\n proposedOperation: operation,\n skipped: true,\n reason: `Premise is already ${premise.status}.`,\n });\n continue;\n }\n actions.push({\n type: action.type,\n entityId,\n currentState: premise.status,\n proposedOperation: operation,\n snapshot: { status: premise.status, updatedAt: premise.updatedAt.toISOString(), assertionText: premise.assertion.text },\n });\n continue;\n }\n\n const intent = await userDb.getIntent(entityId).catch(() => null);\n if (!intent) {\n actions.push(skippedAction(action, entityId, operation, \"Signal not found or not owned by the authenticated user.\"));\n continue;\n }\n const status = intent.status ?? \"ACTIVE\";\n if (intent.archivedAt || status === \"FULFILLED\" || status === \"EXPIRED\") {\n actions.push({\n type: action.type,\n entityId,\n currentState: intent.archivedAt ? \"ARCHIVED\" : status,\n proposedOperation: operation,\n ...(action.type === \"pause_signal\" ? { evidence: action.evidence } : {}),\n skipped: true,\n reason: \"Signal is archived or terminal and cannot be changed.\",\n });\n continue;\n }\n actions.push({\n type: action.type,\n entityId,\n currentState: status,\n proposedOperation: operation,\n ...(action.type === \"pause_signal\" ? { evidence: action.evidence } : {}),\n ...(action.type === \"narrow_signal\" ? { description: action.description } : {}),\n snapshot: {\n status,\n updatedAt: intent.updatedAt.toISOString(),\n payload: intent.payload,\n summary: intent.summary,\n },\n });\n }\n\n await store.createProposal({\n proposalId,\n userId: context.userId,\n ...(context.sessionId ? { conversationId: context.sessionId } : {}),\n actions,\n });\n\n const renderedActions = actions.map(({ snapshot: _snapshot, ...entry }) => entry);\n const block = `\\`\\`\\`${AGENT_ACTION_PROPOSAL_FENCE}\\n${JSON.stringify({ proposalId, actions: renderedActions })}\\n\\`\\`\\``;\n return success({\n proposed: true,\n proposalId,\n actions: renderedActions,\n message: `IMPORTANT: Include this \\`\\`\\`agent_action_proposal code block EXACTLY as-is in your response. It is a REQUEST for owner confirmation; it performs no mutation until the owner confirms it in the UI:\\n\\n${block}`,\n });\n },\n {\n name: \"propose_cleanup_actions\",\n description: \"Prepare owner-confirmed cleanup actions from same-turn grounded reads. This tool never mutates data.\",\n schema: proposeCleanupActionsSchema,\n },\n ) as ChatTools[number];\n}\n\n/** Build the optional action tool from the host-provided ToolContext. */\nexport function createReporterActionTool(\n deps: ToolContext,\n context: ResolvedToolContext,\n userDb: UserDatabase,\n): ChatTools[number] | null {\n if (!deps.actionToolsEnabled || !deps.actionProposalStore) return null;\n return createProposeCleanupActionsTool({\n database: deps.database,\n userDb,\n context,\n store: deps.actionProposalStore,\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.persona.d.ts","sourceRoot":"/","sources":["chat/reporter.persona.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9H,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAE/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,uFAAuF;AACvF,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,kOAWtB,CAAC;AAIX,UAAU,oBAAoB;IAC5B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,oBAAoB,CAAC,EAAE,WAAW,CAAC,sBAAsB,CAAC,CAAC;CAC5D;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE/E;AAMD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,oBAAoB,GAC7B,SAAS,CAkKX;AAED,iEAAiE;AACjE,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,WAAW,EACjB,kBAAkB,CAAC,EAAE,mBAAmB,GACvC,OAAO,CAAC,SAAS,CAAC,CAwBpB;AAED,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,EAAE,iBAQ9B,CAAC"}
1
+ {"version":3,"file":"reporter.persona.d.ts","sourceRoot":"/","sources":["chat/reporter.persona.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9H,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAE/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D,uFAAuF;AACvF,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,kOAWtB,CAAC;AAIX,UAAU,oBAAoB;IAC5B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,oBAAoB,CAAC,EAAE,WAAW,CAAC,sBAAsB,CAAC,CAAC;CAC5D;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE/E;AAMD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,oBAAoB,GAC7B,SAAS,CAkKX;AAED,iEAAiE;AACjE,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,WAAW,EACjB,kBAAkB,CAAC,EAAE,mBAAmB,GACvC,OAAO,CAAC,SAAS,CAAC,CA0BpB;AAED,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,EAAE,iBAQ9B,CAAC"}
@@ -4,6 +4,7 @@ import { createChatTools } from "../shared/agent/tool.factory.js";
4
4
  import { error, resolveChatContext, success } from "../shared/agent/tool.helpers.js";
5
5
  import { focusedNetworkId, scopeFromNetworkId } from "../shared/agent/tool.scope.js";
6
6
  import { buildReporterSystemContent } from "./reporter.prompt.js";
7
+ import { createReporterActionTool } from "./reporter.action.tools.js";
7
8
  /** Public kickoff marker used by the Agent surface to request its opening briefing. */
8
9
  export { REPORTER_BRIEFING_KICKOFF } from "./reporter.prompt.js";
9
10
  /** Stable persona id persisted for read-only Agent reporting sessions. */
@@ -179,6 +180,7 @@ export async function createReporterTools(deps, preResolvedContext) {
179
180
  networkId: explicitScope.scopeType === "network" ? explicitScope.scopeId : deps.networkId,
180
181
  sessionId: deps.sessionId,
181
182
  contactsEnabled: deps.contactsEnabled,
183
+ actionToolsEnabled: deps.actionToolsEnabled,
182
184
  });
183
185
  if (explicitScope.scopeType && explicitScope.scopeId) {
184
186
  resolvedContext.scopeType = explicitScope.scopeType;
@@ -186,11 +188,13 @@ export async function createReporterTools(deps, preResolvedContext) {
186
188
  }
187
189
  const userDb = deps.userDb ?? deps.createUserDatabase(deps.database, resolvedContext.userId);
188
190
  const allowed = filterReporterTools(await createChatTools(deps, resolvedContext));
189
- return narrowReporterTools(allowed, {
191
+ const narrowed = narrowReporterTools(allowed, {
190
192
  context: resolvedContext,
191
193
  userDb,
192
194
  findPendingQuestions: deps.findPendingQuestions,
193
195
  });
196
+ const actionTool = createReporterActionTool(deps, resolvedContext, userDb);
197
+ return actionTool ? [...narrowed, actionTool] : narrowed;
194
198
  }
195
199
  /** Restricted read-only Agent reporter persona. */
196
200
  export const REPORTER_PERSONA = {
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.persona.js","sourceRoot":"/","sources":["chat/reporter.persona.ts"],"names":[],"mappings":"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","sourcesContent":["import { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\n\nimport { createChatTools, type ChatTools, type ResolvedToolContext, type ToolContext } from \"../shared/agent/tool.factory.js\";\nimport { error, resolveChatContext, success } from \"../shared/agent/tool.helpers.js\";\nimport type { UserDatabase } from \"../shared/interfaces/database.interface.js\";\nimport { focusedNetworkId, scopeFromNetworkId } from \"../shared/agent/tool.scope.js\";\nimport type { ChatPersonaConfig } from \"./chat.persona.js\";\nimport { buildReporterSystemContent } from \"./reporter.prompt.js\";\n\n/** Public kickoff marker used by the Agent surface to request its opening briefing. */\nexport { REPORTER_BRIEFING_KICKOFF } from \"./reporter.prompt.js\";\n\n/** Stable persona id persisted for read-only Agent reporting sessions. */\nexport const REPORTER_PERSONA_ID = \"reporter\";\n\n/**\n * Exact positive allowlist for the reporter persona. New shared tools remain\n * unavailable until they are explicitly reviewed here.\n */\nexport const REPORTER_TOOL_NAMES = [\n \"read_intents\",\n \"search_intents\",\n \"read_user_contexts\",\n \"preview_user_context\",\n \"read_premises\",\n \"read_networks\",\n \"read_network_memberships\",\n \"read_pending_questions\",\n \"list_opportunities\",\n \"report_agent_activity\",\n] as const;\n\nconst REPORTER_TOOL_ALLOWLIST: ReadonlySet<string> = new Set(REPORTER_TOOL_NAMES);\n\ninterface ReporterToolBoundary {\n context: ResolvedToolContext;\n userDb: UserDatabase;\n findPendingQuestions?: ToolContext[\"findPendingQuestions\"];\n}\n\n/** Filters a shared registry through the reporter's positive allowlist. */\nexport function filterReporterTools<T extends { name: string }>(tools: T[]): T[] {\n return tools.filter((candidate) => REPORTER_TOOL_ALLOWLIST.has(candidate.name));\n}\n\nfunction invokeSharedTool(sharedTool: { invoke: (input: unknown) => unknown }, input: unknown): Promise<string> {\n return sharedTool.invoke(input) as Promise<string>;\n}\n\n/**\n * Replaces shared tools whose normal modes can enumerate other users with\n * reporter-safe, self-only read contracts. Opportunity listing is deliberately\n * aggregate-only: it never returns a counterpart name, row, or explanation.\n */\nexport function narrowReporterTools(\n allowed: ChatTools,\n boundary: ReporterToolBoundary,\n): ChatTools {\n const { context, userDb } = boundary;\n\n return allowed.map((sharedTool) => {\n if (sharedTool.name === \"read_intents\") {\n return tool(\n async (query: { limit?: number; page?: number }) => {\n const limit = query.limit ?? 100;\n const page = query.page ?? 1;\n const intents = await userDb.getActiveIntents();\n const start = (page - 1) * limit;\n return success({\n intents: intents.slice(start, start + limit),\n page,\n limit,\n total: intents.length,\n });\n },\n {\n name: \"read_intents\",\n description: \"Read the authenticated user's own active signals only.\",\n schema: z.object({\n limit: z.number().int().min(1).max(100).optional(),\n page: z.number().int().min(1).optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"search_intents\") {\n return tool(\n async (query: { query: string; limit?: number }) => success({\n intents: await userDb.searchOwnIntents(query.query, query.limit ?? 20),\n }),\n {\n name: \"search_intents\",\n description: \"Search the authenticated user's own active signals only.\",\n schema: z.object({\n query: z.string().trim().min(1),\n limit: z.number().int().min(1).max(100).optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_user_contexts\") {\n return tool(\n async () => invokeSharedTool(sharedTool, {}),\n {\n name: \"read_user_contexts\",\n description: \"Read the authenticated user's own identity and global context only.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_premises\") {\n return tool(\n async (query: { includeRetracted?: boolean }) => invokeSharedTool(sharedTool, {\n includeRetracted: query.includeRetracted ?? false,\n userId: context.userId,\n }),\n {\n name: \"read_premises\",\n description: \"Read the authenticated user's own premises only.\",\n schema: z.object({ includeRetracted: z.boolean().optional() }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_network_memberships\") {\n return tool(\n async () => invokeSharedTool(sharedTool, {}),\n {\n name: \"read_network_memberships\",\n description: \"Read the authenticated user's own network memberships only.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_networks\") {\n return tool(\n async () => invokeSharedTool(sharedTool, { userId: context.userId }),\n {\n name: \"read_networks\",\n description: \"Read networks available to the authenticated user.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_pending_questions\") {\n return tool(\n async (query: { limit?: number }) => {\n if (!boundary.findPendingQuestions) return error(\"Question lookup is not available.\");\n const questions = await boundary.findPendingQuestions(context.userId, {\n modes: [\"enrichment\", \"intent\", \"discovery\"],\n limit: query.limit ?? 10,\n });\n return success({ questions: questions.slice(0, query.limit ?? 10) });\n },\n {\n name: \"read_pending_questions\",\n description: \"Read the user's own non-negotiation pending questions; answering is unavailable here.\",\n schema: z.object({ limit: z.number().int().min(1).max(10).optional() }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"list_opportunities\") {\n return tool(\n async (query: { networkId?: string }) => {\n const scopedNetworkId = focusedNetworkId(context);\n if (scopedNetworkId && query.networkId && query.networkId !== scopedNetworkId) {\n return error(\"This chat is scoped to a different network.\");\n }\n const activeIntents = await userDb.getActiveIntents();\n const intentById = new Map(activeIntents.map((intent) => [intent.id, intent]));\n const opportunities = await userDb.getOpportunitiesForUser({\n ...(query.networkId || scopedNetworkId ? { networkId: query.networkId || scopedNetworkId } : {}),\n statuses: [\"draft\", \"pending\", \"latent\"],\n limit: 100,\n });\n const counts = new Map<string, { intentId: string; title: string; count: number }>();\n const seen = new Set<string>();\n for (const opportunity of opportunities) {\n const ownIntentIds = new Set(\n opportunity.actors\n .filter((actor) => actor.userId === context.userId && actor.intent && intentById.has(actor.intent))\n .map((actor) => actor.intent as string),\n );\n if (ownIntentIds.size === 0) continue;\n seen.add(opportunity.id);\n for (const intentId of ownIntentIds) {\n const intent = intentById.get(intentId);\n if (!intent) continue;\n const existing = counts.get(intentId) ?? {\n intentId,\n title: intent.summary?.trim() || intent.payload,\n count: 0,\n };\n existing.count += 1;\n counts.set(intentId, existing);\n }\n }\n return success({\n found: seen.size > 0,\n count: seen.size,\n bySignal: [...counts.values()],\n });\n },\n {\n name: \"list_opportunities\",\n description: \"Report current opportunity counts by the user's own signal, without counterpart identities or rows.\",\n schema: z.object({ networkId: z.string().uuid().optional() }).strict(),\n },\n );\n }\n\n return sharedTool;\n }) as ChatTools;\n}\n\n/** Creates the reporter's context-bound, allowlisted toolset. */\nexport async function createReporterTools(\n deps: ToolContext,\n preResolvedContext?: ResolvedToolContext,\n): Promise<ChatTools> {\n const explicitScope = deps.scopeType && deps.scopeId\n ? { scopeType: deps.scopeType, scopeId: deps.scopeId }\n : scopeFromNetworkId(deps.networkId);\n const resolvedContext = preResolvedContext ?? await resolveChatContext({\n database: deps.database,\n userId: deps.userId,\n networkId: explicitScope.scopeType === \"network\" ? explicitScope.scopeId : deps.networkId,\n sessionId: deps.sessionId,\n contactsEnabled: deps.contactsEnabled,\n });\n if (explicitScope.scopeType && explicitScope.scopeId) {\n resolvedContext.scopeType = explicitScope.scopeType;\n resolvedContext.scopeId = explicitScope.scopeId;\n }\n\n const userDb = deps.userDb ?? deps.createUserDatabase(deps.database, resolvedContext.userId);\n const allowed = filterReporterTools(await createChatTools(deps, resolvedContext)) as ChatTools;\n\n return narrowReporterTools(allowed, {\n context: resolvedContext,\n userDb,\n findPendingQuestions: deps.findPendingQuestions,\n });\n}\n\n/** Restricted read-only Agent reporter persona. */\nexport const REPORTER_PERSONA: ChatPersonaConfig = {\n id: REPORTER_PERSONA_ID,\n buildSystemContent: (ctx, iterCtx) => buildReporterSystemContent(ctx, iterCtx),\n createTools: (deps, preResolvedContext) => createReporterTools(deps, preResolvedContext),\n loopBehaviors: {\n createIntentCallback: false,\n hallucinationRecovery: false,\n },\n};\n"]}
1
+ {"version":3,"file":"reporter.persona.js","sourceRoot":"/","sources":["chat/reporter.persona.ts"],"names":[],"mappings":"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","sourcesContent":["import { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\n\nimport { createChatTools, type ChatTools, type ResolvedToolContext, type ToolContext } from \"../shared/agent/tool.factory.js\";\nimport { error, resolveChatContext, success } from \"../shared/agent/tool.helpers.js\";\nimport type { UserDatabase } from \"../shared/interfaces/database.interface.js\";\nimport { focusedNetworkId, scopeFromNetworkId } from \"../shared/agent/tool.scope.js\";\nimport type { ChatPersonaConfig } from \"./chat.persona.js\";\nimport { buildReporterSystemContent } from \"./reporter.prompt.js\";\nimport { createReporterActionTool } from \"./reporter.action.tools.js\";\n\n/** Public kickoff marker used by the Agent surface to request its opening briefing. */\nexport { REPORTER_BRIEFING_KICKOFF } from \"./reporter.prompt.js\";\n\n/** Stable persona id persisted for read-only Agent reporting sessions. */\nexport const REPORTER_PERSONA_ID = \"reporter\";\n\n/**\n * Exact positive allowlist for the reporter persona. New shared tools remain\n * unavailable until they are explicitly reviewed here.\n */\nexport const REPORTER_TOOL_NAMES = [\n \"read_intents\",\n \"search_intents\",\n \"read_user_contexts\",\n \"preview_user_context\",\n \"read_premises\",\n \"read_networks\",\n \"read_network_memberships\",\n \"read_pending_questions\",\n \"list_opportunities\",\n \"report_agent_activity\",\n] as const;\n\nconst REPORTER_TOOL_ALLOWLIST: ReadonlySet<string> = new Set(REPORTER_TOOL_NAMES);\n\ninterface ReporterToolBoundary {\n context: ResolvedToolContext;\n userDb: UserDatabase;\n findPendingQuestions?: ToolContext[\"findPendingQuestions\"];\n}\n\n/** Filters a shared registry through the reporter's positive allowlist. */\nexport function filterReporterTools<T extends { name: string }>(tools: T[]): T[] {\n return tools.filter((candidate) => REPORTER_TOOL_ALLOWLIST.has(candidate.name));\n}\n\nfunction invokeSharedTool(sharedTool: { invoke: (input: unknown) => unknown }, input: unknown): Promise<string> {\n return sharedTool.invoke(input) as Promise<string>;\n}\n\n/**\n * Replaces shared tools whose normal modes can enumerate other users with\n * reporter-safe, self-only read contracts. Opportunity listing is deliberately\n * aggregate-only: it never returns a counterpart name, row, or explanation.\n */\nexport function narrowReporterTools(\n allowed: ChatTools,\n boundary: ReporterToolBoundary,\n): ChatTools {\n const { context, userDb } = boundary;\n\n return allowed.map((sharedTool) => {\n if (sharedTool.name === \"read_intents\") {\n return tool(\n async (query: { limit?: number; page?: number }) => {\n const limit = query.limit ?? 100;\n const page = query.page ?? 1;\n const intents = await userDb.getActiveIntents();\n const start = (page - 1) * limit;\n return success({\n intents: intents.slice(start, start + limit),\n page,\n limit,\n total: intents.length,\n });\n },\n {\n name: \"read_intents\",\n description: \"Read the authenticated user's own active signals only.\",\n schema: z.object({\n limit: z.number().int().min(1).max(100).optional(),\n page: z.number().int().min(1).optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"search_intents\") {\n return tool(\n async (query: { query: string; limit?: number }) => success({\n intents: await userDb.searchOwnIntents(query.query, query.limit ?? 20),\n }),\n {\n name: \"search_intents\",\n description: \"Search the authenticated user's own active signals only.\",\n schema: z.object({\n query: z.string().trim().min(1),\n limit: z.number().int().min(1).max(100).optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_user_contexts\") {\n return tool(\n async () => invokeSharedTool(sharedTool, {}),\n {\n name: \"read_user_contexts\",\n description: \"Read the authenticated user's own identity and global context only.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_premises\") {\n return tool(\n async (query: { includeRetracted?: boolean }) => invokeSharedTool(sharedTool, {\n includeRetracted: query.includeRetracted ?? false,\n userId: context.userId,\n }),\n {\n name: \"read_premises\",\n description: \"Read the authenticated user's own premises only.\",\n schema: z.object({ includeRetracted: z.boolean().optional() }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_network_memberships\") {\n return tool(\n async () => invokeSharedTool(sharedTool, {}),\n {\n name: \"read_network_memberships\",\n description: \"Read the authenticated user's own network memberships only.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_networks\") {\n return tool(\n async () => invokeSharedTool(sharedTool, { userId: context.userId }),\n {\n name: \"read_networks\",\n description: \"Read networks available to the authenticated user.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_pending_questions\") {\n return tool(\n async (query: { limit?: number }) => {\n if (!boundary.findPendingQuestions) return error(\"Question lookup is not available.\");\n const questions = await boundary.findPendingQuestions(context.userId, {\n modes: [\"enrichment\", \"intent\", \"discovery\"],\n limit: query.limit ?? 10,\n });\n return success({ questions: questions.slice(0, query.limit ?? 10) });\n },\n {\n name: \"read_pending_questions\",\n description: \"Read the user's own non-negotiation pending questions; answering is unavailable here.\",\n schema: z.object({ limit: z.number().int().min(1).max(10).optional() }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"list_opportunities\") {\n return tool(\n async (query: { networkId?: string }) => {\n const scopedNetworkId = focusedNetworkId(context);\n if (scopedNetworkId && query.networkId && query.networkId !== scopedNetworkId) {\n return error(\"This chat is scoped to a different network.\");\n }\n const activeIntents = await userDb.getActiveIntents();\n const intentById = new Map(activeIntents.map((intent) => [intent.id, intent]));\n const opportunities = await userDb.getOpportunitiesForUser({\n ...(query.networkId || scopedNetworkId ? { networkId: query.networkId || scopedNetworkId } : {}),\n statuses: [\"draft\", \"pending\", \"latent\"],\n limit: 100,\n });\n const counts = new Map<string, { intentId: string; title: string; count: number }>();\n const seen = new Set<string>();\n for (const opportunity of opportunities) {\n const ownIntentIds = new Set(\n opportunity.actors\n .filter((actor) => actor.userId === context.userId && actor.intent && intentById.has(actor.intent))\n .map((actor) => actor.intent as string),\n );\n if (ownIntentIds.size === 0) continue;\n seen.add(opportunity.id);\n for (const intentId of ownIntentIds) {\n const intent = intentById.get(intentId);\n if (!intent) continue;\n const existing = counts.get(intentId) ?? {\n intentId,\n title: intent.summary?.trim() || intent.payload,\n count: 0,\n };\n existing.count += 1;\n counts.set(intentId, existing);\n }\n }\n return success({\n found: seen.size > 0,\n count: seen.size,\n bySignal: [...counts.values()],\n });\n },\n {\n name: \"list_opportunities\",\n description: \"Report current opportunity counts by the user's own signal, without counterpart identities or rows.\",\n schema: z.object({ networkId: z.string().uuid().optional() }).strict(),\n },\n );\n }\n\n return sharedTool;\n }) as ChatTools;\n}\n\n/** Creates the reporter's context-bound, allowlisted toolset. */\nexport async function createReporterTools(\n deps: ToolContext,\n preResolvedContext?: ResolvedToolContext,\n): Promise<ChatTools> {\n const explicitScope = deps.scopeType && deps.scopeId\n ? { scopeType: deps.scopeType, scopeId: deps.scopeId }\n : scopeFromNetworkId(deps.networkId);\n const resolvedContext = preResolvedContext ?? await resolveChatContext({\n database: deps.database,\n userId: deps.userId,\n networkId: explicitScope.scopeType === \"network\" ? explicitScope.scopeId : deps.networkId,\n sessionId: deps.sessionId,\n contactsEnabled: deps.contactsEnabled,\n actionToolsEnabled: deps.actionToolsEnabled,\n });\n if (explicitScope.scopeType && explicitScope.scopeId) {\n resolvedContext.scopeType = explicitScope.scopeType;\n resolvedContext.scopeId = explicitScope.scopeId;\n }\n\n const userDb = deps.userDb ?? deps.createUserDatabase(deps.database, resolvedContext.userId);\n const allowed = filterReporterTools(await createChatTools(deps, resolvedContext)) as ChatTools;\n const narrowed = narrowReporterTools(allowed, {\n context: resolvedContext,\n userDb,\n findPendingQuestions: deps.findPendingQuestions,\n });\n const actionTool = createReporterActionTool(deps, resolvedContext, userDb);\n return actionTool ? [...narrowed, actionTool] as ChatTools : narrowed;\n}\n\n/** Restricted read-only Agent reporter persona. */\nexport const REPORTER_PERSONA: ChatPersonaConfig = {\n id: REPORTER_PERSONA_ID,\n buildSystemContent: (ctx, iterCtx) => buildReporterSystemContent(ctx, iterCtx),\n createTools: (deps, preResolvedContext) => createReporterTools(deps, preResolvedContext),\n loopBehaviors: {\n createIntentCallback: false,\n hallucinationRecovery: false,\n },\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.prompt.d.ts","sourceRoot":"/","sources":["chat/reporter.prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAYnE;AAgBD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,mBAAmB,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CA8DR"}
1
+ {"version":3,"file":"reporter.prompt.d.ts","sourceRoot":"/","sources":["chat/reporter.prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAYnE;AAgBD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,mBAAmB,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CA6ER"}
@@ -50,15 +50,30 @@ export function buildReporterSystemContent(ctx, iterCtx) {
50
50
  title: network.networkTitle,
51
51
  isPersonal: network.isPersonal,
52
52
  })), null, 2);
53
+ const roleGuidance = ctx.actionToolsEnabled
54
+ ? "Your role is to report what the user's Index agent has done and what the user's own signals currently communicate. You may prepare a cleanup-action request from grounded same-turn reads, but you never change anything in chat."
55
+ : "Your role is to report what the user's Index agent has done and what the user's own signals currently communicate. You observe; you never change anything. Suggestions such as pausing or merging a signal are recommendations for the user to carry out through existing product UI, never actions for this persona.";
56
+ const mutationRule = ctx.actionToolsEnabled
57
+ ? "- Never mutate data in chat. You may call propose_cleanup_actions only after same-turn owner-scoped reads; it creates a REQUEST block and never executes an action. The owner must confirm through the product UI."
58
+ : "- Never create, update, delete, confirm, answer, remember, forget, assign, discover, negotiate, scrape, or otherwise mutate data. Do not ask the user a question through a tool.";
59
+ const actionGuidance = ctx.actionToolsEnabled ? `
60
+
61
+ ## Cleanup-action requests
62
+ - You may propose only retract_premise, narrow_signal, and pause_signal actions grounded in read results from this same turn.
63
+ - Resolve references through owner-scoped read tools and pass exact full UUIDs only; never use suffixes, guesses, or IDs from another user.
64
+ - pause_signal requires non-empty evidence recorded from this turn, such as zero live opportunities plus the owner's statement.
65
+ - The proposal block is a REQUEST for owner confirmation. Never narrate an action as completed and never mutate inside chat.
66
+ - Do not propose actions for counterparties or expose counterparty identity.
67
+ ` : "";
53
68
  return `You are Agent, the user's private read-only activity reporter for ${ctx.userName}.
54
69
 
55
- Your role is to report what the user's Index agent has done and what the user's own signals currently communicate. You observe; you never change anything. Suggestions such as pausing or merging a signal are recommendations for the user to carry out through existing product UI, never actions for this persona.
70
+ ${roleGuidance}${actionGuidance}
56
71
 
57
72
  ## Hard rules
58
73
  - Every factual claim, number, status, or trend must come from a tool result in the current turn. Never invent, estimate, or reuse an unverified metric.
59
74
  - Use report_agent_activity for activity counts and read_intents/read_user_contexts/read_premises/read_networks/read_network_memberships/read_pending_questions for the underlying current state.
60
75
  - Counterparties are identity-free aggregate data only: never reveal names, IDs, transcripts, message text, or per-counterparty rows. Do not infer what another person thinks from a match or negotiation.
61
- - Never create, update, delete, confirm, answer, remember, forget, assign, discover, negotiate, scrape, or otherwise mutate data. Do not ask the user a question through a tool.
76
+ ${mutationRule}
62
77
  - Do not write observed behavior back as a preference or premise. The user decides whether to act on a suggestion.
63
78
  - If opportunity information is relevant, use only the restricted list_opportunities result or report_agent_activity result. Do not expose raw evaluator reasoning, matchReason, or internal JSON. Any opportunity copy must be presenter-backed; this persona's list view is aggregate-only.
64
79
  - Be transparent about missing data and the reporting window. Keep the response concise, calm, and useful without hype.
@@ -76,7 +91,7 @@ Be ready to answer:
76
91
  - Own community context: read_networks, read_network_memberships.
77
92
  - Own pending-question reads: read_pending_questions (never answer them).
78
93
  - Aggregate activity reporting: report_agent_activity.
79
- - Aggregate current opportunity reporting: list_opportunities (no counterpart identities or rows).
94
+ - Aggregate current opportunity reporting: list_opportunities (no counterpart identities or rows).${ctx.actionToolsEnabled ? "\n- Cleanup-action requests: propose_cleanup_actions (request only; owner confirmation is required)." : ""}
80
95
 
81
96
  ## Session identity (preloaded)
82
97
  - User: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.prompt.js","sourceRoot":"/","sources":["chat/reporter.prompt.ts"],"names":[],"mappings":"AAGA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAgB;IACxD,MAAM,UAAU,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,iBAAiB,EAAE;SACnD,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAE9B,OAAO,IAAI,GAAG,CAAC;QACb,yBAAyB;QACzB,mBAAmB;QACnB,mBAAmB;QACnB,gBAAgB;KACjB,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,qBAAqB,CAAC,OAA0B;IACvD,IAAI,CAAC,yBAAyB,CAAC,OAAO,EAAE,cAAc,CAAC;QAAE,OAAO,EAAE,CAAC;IAEnE,OAAO;;;;;;;;0JAQiJ,CAAC;AAC3J,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,GAAwB,EACxB,OAA0B;IAE1B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,GAAG,CAAC,WAAW;QACpC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC,MAAM,CAAC;IACX,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CACtC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACjC,EAAE,EAAE,OAAO,CAAC,SAAS;QACrB,KAAK,EAAE,OAAO,CAAC,YAAY;QAC3B,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAC,EACH,IAAI,EACJ,CAAC,CACF,CAAC;IAEF,OAAO,qEAAqE,GAAG,CAAC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6BhF,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,SAAS,UAAU,GAAG,CAAC,MAAM;;;;EAI1D,WAAW;;;;;EAKX,cAAc;;;;;EAKd,iBAAiB;;;sMAGmL,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;AACvO,CAAC","sourcesContent":["import type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\nimport type { IterationContext } from \"./chat.prompt.modules.js\";\n\n/** Stable marker used by the Agent surface to request its opening briefing. */\nexport const REPORTER_BRIEFING_KICKOFF = \"reporter-briefing-kickoff\";\n\n/**\n * Recognizes the explicit opening briefing marker without putting ordinary\n * reporter conversations into briefing mode accidentally.\n *\n * @param message - Latest user message in the current turn\n * @returns Whether this turn is the Agent-surface briefing kickoff\n */\nexport function isReporterBriefingKickoff(message?: string): boolean {\n const normalized = message?.trim().toLocaleLowerCase()\n .replace(/[–—]/g, \"-\")\n .replace(/^_+|_+$/g, \"\");\n if (!normalized) return false;\n\n return new Set([\n REPORTER_BRIEFING_KICKOFF,\n \"reporter-briefing\",\n \"reporter briefing\",\n \"agent briefing\",\n ]).has(normalized);\n}\n\nfunction buildBriefingGuidance(iterCtx?: IterationContext): string {\n if (!isReporterBriefingKickoff(iterCtx?.currentMessage)) return \"\";\n\n return `\n\n## Opening briefing\nThis is the Agent-surface briefing kickoff. Call report_agent_activity first with the default window, then call the read tools needed to ground the four transparency asks below. Present one concise briefing covering:\n1. summarize all my signals;\n2. what did you do today?;\n3. how do I look to others?;\n4. what should I sharpen?\nDo not claim a metric unless it appears in a tool result from this turn. If a section has no grounded data, say that plainly rather than filling the gap.`;\n}\n\n/**\n * Builds the read-only reporter persona prompt.\n *\n * @param ctx - Resolved authenticated user context\n * @param iterCtx - Current agent-loop context used for briefing kickoff\n * @returns Complete reporter system content\n */\nexport function buildReporterSystemContent(\n ctx: ResolvedToolContext,\n iterCtx?: IterationContext,\n): string {\n const userContext = JSON.stringify(ctx.user, null, 2);\n const profileContext = ctx.userProfile\n ? JSON.stringify(ctx.userProfile, null, 2)\n : \"null\";\n const membershipContext = JSON.stringify(\n ctx.userNetworks.map((network) => ({\n id: network.networkId,\n title: network.networkTitle,\n isPersonal: network.isPersonal,\n })),\n null,\n 2,\n );\n\n return `You are Agent, the user's private read-only activity reporter for ${ctx.userName}.\n\nYour role is to report what the user's Index agent has done and what the user's own signals currently communicate. You observe; you never change anything. Suggestions such as pausing or merging a signal are recommendations for the user to carry out through existing product UI, never actions for this persona.\n\n## Hard rules\n- Every factual claim, number, status, or trend must come from a tool result in the current turn. Never invent, estimate, or reuse an unverified metric.\n- Use report_agent_activity for activity counts and read_intents/read_user_contexts/read_premises/read_networks/read_network_memberships/read_pending_questions for the underlying current state.\n- Counterparties are identity-free aggregate data only: never reveal names, IDs, transcripts, message text, or per-counterparty rows. Do not infer what another person thinks from a match or negotiation.\n- Never create, update, delete, confirm, answer, remember, forget, assign, discover, negotiate, scrape, or otherwise mutate data. Do not ask the user a question through a tool.\n- Do not write observed behavior back as a preference or premise. The user decides whether to act on a suggestion.\n- If opportunity information is relevant, use only the restricted list_opportunities result or report_agent_activity result. Do not expose raw evaluator reasoning, matchReason, or internal JSON. Any opportunity copy must be presenter-backed; this persona's list view is aggregate-only.\n- Be transparent about missing data and the reporting window. Keep the response concise, calm, and useful without hype.\n\n## Four transparency asks\nBe ready to answer:\n- “summarize all my signals” — read the user's own signals and describe their current themes without inventing a synthesis.\n- “what did you do today?” — report only grounded activity counts from the requested window.\n- “how do I look to others?” — describe only the user's own stored context and signals; do not claim access to private counterparty opinions.\n- “what should I sharpen?” — suggest possible signal/context improvements based on observed gaps in the returned data, clearly label them as suggestions, and point the user to existing UI. Never apply them.\n\n## Allowed capabilities\n- Own signal reads: read_intents, search_intents.\n- Own context reads: read_user_contexts, preview_user_context, read_premises.\n- Own community context: read_networks, read_network_memberships.\n- Own pending-question reads: read_pending_questions (never answer them).\n- Aggregate activity reporting: report_agent_activity.\n- Aggregate current opportunity reporting: list_opportunities (no counterpart identities or rows).\n\n## Session identity (preloaded)\n- User: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}\n\n### User record\n\\`\\`\\`json\n${userContext}\n\\`\\`\\`\n\n### User context\n\\`\\`\\`json\n${profileContext}\n\\`\\`\\`\n\n### Current memberships\n\\`\\`\\`json\n${membershipContext}\n\\`\\`\\`\n\nOnly the identity, context, and membership metadata above are preloaded. Signals, premises, questions, and activity are not preloaded: call the appropriate read/report tool before describing them.${buildBriefingGuidance(iterCtx)}`;\n}\n"]}
1
+ {"version":3,"file":"reporter.prompt.js","sourceRoot":"/","sources":["chat/reporter.prompt.ts"],"names":[],"mappings":"AAGA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAgB;IACxD,MAAM,UAAU,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,iBAAiB,EAAE;SACnD,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAE9B,OAAO,IAAI,GAAG,CAAC;QACb,yBAAyB;QACzB,mBAAmB;QACnB,mBAAmB;QACnB,gBAAgB;KACjB,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,qBAAqB,CAAC,OAA0B;IACvD,IAAI,CAAC,yBAAyB,CAAC,OAAO,EAAE,cAAc,CAAC;QAAE,OAAO,EAAE,CAAC;IAEnE,OAAO;;;;;;;;0JAQiJ,CAAC;AAC3J,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,GAAwB,EACxB,OAA0B;IAE1B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,GAAG,CAAC,WAAW;QACpC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC,MAAM,CAAC;IACX,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CACtC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACjC,EAAE,EAAE,OAAO,CAAC,SAAS;QACrB,KAAK,EAAE,OAAO,CAAC,YAAY;QAC3B,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAC,EACH,IAAI,EACJ,CAAC,CACF,CAAC;IACF,MAAM,YAAY,GAAG,GAAG,CAAC,kBAAkB;QACzC,CAAC,CAAC,mOAAmO;QACrO,CAAC,CAAC,uTAAuT,CAAC;IAC5T,MAAM,YAAY,GAAG,GAAG,CAAC,kBAAkB;QACzC,CAAC,CAAC,oNAAoN;QACtN,CAAC,CAAC,kLAAkL,CAAC;IACvL,MAAM,cAAc,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;;;;;;;;CAQjD,CAAC,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,qEAAqE,GAAG,CAAC,QAAQ;;EAExF,YAAY,GAAG,cAAc;;;;;;EAM7B,YAAY;;;;;;;;;;;;;;;;;;oGAkBsF,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,sGAAsG,CAAC,CAAC,CAAC,EAAE;;;UAG9N,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,SAAS,UAAU,GAAG,CAAC,MAAM;;;;EAI1D,WAAW;;;;;EAKX,cAAc;;;;;EAKd,iBAAiB;;;sMAGmL,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;AACvO,CAAC","sourcesContent":["import type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\nimport type { IterationContext } from \"./chat.prompt.modules.js\";\n\n/** Stable marker used by the Agent surface to request its opening briefing. */\nexport const REPORTER_BRIEFING_KICKOFF = \"reporter-briefing-kickoff\";\n\n/**\n * Recognizes the explicit opening briefing marker without putting ordinary\n * reporter conversations into briefing mode accidentally.\n *\n * @param message - Latest user message in the current turn\n * @returns Whether this turn is the Agent-surface briefing kickoff\n */\nexport function isReporterBriefingKickoff(message?: string): boolean {\n const normalized = message?.trim().toLocaleLowerCase()\n .replace(/[–—]/g, \"-\")\n .replace(/^_+|_+$/g, \"\");\n if (!normalized) return false;\n\n return new Set([\n REPORTER_BRIEFING_KICKOFF,\n \"reporter-briefing\",\n \"reporter briefing\",\n \"agent briefing\",\n ]).has(normalized);\n}\n\nfunction buildBriefingGuidance(iterCtx?: IterationContext): string {\n if (!isReporterBriefingKickoff(iterCtx?.currentMessage)) return \"\";\n\n return `\n\n## Opening briefing\nThis is the Agent-surface briefing kickoff. Call report_agent_activity first with the default window, then call the read tools needed to ground the four transparency asks below. Present one concise briefing covering:\n1. summarize all my signals;\n2. what did you do today?;\n3. how do I look to others?;\n4. what should I sharpen?\nDo not claim a metric unless it appears in a tool result from this turn. If a section has no grounded data, say that plainly rather than filling the gap.`;\n}\n\n/**\n * Builds the read-only reporter persona prompt.\n *\n * @param ctx - Resolved authenticated user context\n * @param iterCtx - Current agent-loop context used for briefing kickoff\n * @returns Complete reporter system content\n */\nexport function buildReporterSystemContent(\n ctx: ResolvedToolContext,\n iterCtx?: IterationContext,\n): string {\n const userContext = JSON.stringify(ctx.user, null, 2);\n const profileContext = ctx.userProfile\n ? JSON.stringify(ctx.userProfile, null, 2)\n : \"null\";\n const membershipContext = JSON.stringify(\n ctx.userNetworks.map((network) => ({\n id: network.networkId,\n title: network.networkTitle,\n isPersonal: network.isPersonal,\n })),\n null,\n 2,\n );\n const roleGuidance = ctx.actionToolsEnabled\n ? \"Your role is to report what the user's Index agent has done and what the user's own signals currently communicate. You may prepare a cleanup-action request from grounded same-turn reads, but you never change anything in chat.\"\n : \"Your role is to report what the user's Index agent has done and what the user's own signals currently communicate. You observe; you never change anything. Suggestions such as pausing or merging a signal are recommendations for the user to carry out through existing product UI, never actions for this persona.\";\n const mutationRule = ctx.actionToolsEnabled\n ? \"- Never mutate data in chat. You may call propose_cleanup_actions only after same-turn owner-scoped reads; it creates a REQUEST block and never executes an action. The owner must confirm through the product UI.\"\n : \"- Never create, update, delete, confirm, answer, remember, forget, assign, discover, negotiate, scrape, or otherwise mutate data. Do not ask the user a question through a tool.\";\n const actionGuidance = ctx.actionToolsEnabled ? `\n\n## Cleanup-action requests\n- You may propose only retract_premise, narrow_signal, and pause_signal actions grounded in read results from this same turn.\n- Resolve references through owner-scoped read tools and pass exact full UUIDs only; never use suffixes, guesses, or IDs from another user.\n- pause_signal requires non-empty evidence recorded from this turn, such as zero live opportunities plus the owner's statement.\n- The proposal block is a REQUEST for owner confirmation. Never narrate an action as completed and never mutate inside chat.\n- Do not propose actions for counterparties or expose counterparty identity.\n` : \"\";\n\n return `You are Agent, the user's private read-only activity reporter for ${ctx.userName}.\n\n${roleGuidance}${actionGuidance}\n\n## Hard rules\n- Every factual claim, number, status, or trend must come from a tool result in the current turn. Never invent, estimate, or reuse an unverified metric.\n- Use report_agent_activity for activity counts and read_intents/read_user_contexts/read_premises/read_networks/read_network_memberships/read_pending_questions for the underlying current state.\n- Counterparties are identity-free aggregate data only: never reveal names, IDs, transcripts, message text, or per-counterparty rows. Do not infer what another person thinks from a match or negotiation.\n${mutationRule}\n- Do not write observed behavior back as a preference or premise. The user decides whether to act on a suggestion.\n- If opportunity information is relevant, use only the restricted list_opportunities result or report_agent_activity result. Do not expose raw evaluator reasoning, matchReason, or internal JSON. Any opportunity copy must be presenter-backed; this persona's list view is aggregate-only.\n- Be transparent about missing data and the reporting window. Keep the response concise, calm, and useful without hype.\n\n## Four transparency asks\nBe ready to answer:\n- “summarize all my signals” — read the user's own signals and describe their current themes without inventing a synthesis.\n- “what did you do today?” — report only grounded activity counts from the requested window.\n- “how do I look to others?” — describe only the user's own stored context and signals; do not claim access to private counterparty opinions.\n- “what should I sharpen?” — suggest possible signal/context improvements based on observed gaps in the returned data, clearly label them as suggestions, and point the user to existing UI. Never apply them.\n\n## Allowed capabilities\n- Own signal reads: read_intents, search_intents.\n- Own context reads: read_user_contexts, preview_user_context, read_premises.\n- Own community context: read_networks, read_network_memberships.\n- Own pending-question reads: read_pending_questions (never answer them).\n- Aggregate activity reporting: report_agent_activity.\n- Aggregate current opportunity reporting: list_opportunities (no counterpart identities or rows).${ctx.actionToolsEnabled ? \"\\n- Cleanup-action requests: propose_cleanup_actions (request only; owner confirmation is required).\" : \"\"}\n\n## Session identity (preloaded)\n- User: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}\n\n### User record\n\\`\\`\\`json\n${userContext}\n\\`\\`\\`\n\n### User context\n\\`\\`\\`json\n${profileContext}\n\\`\\`\\`\n\n### Current memberships\n\\`\\`\\`json\n${membershipContext}\n\\`\\`\\`\n\nOnly the identity, context, and membership metadata above are preloaded. Signals, premises, questions, and activity are not preloaded: call the appropriate read/report tool before describing them.${buildBriefingGuidance(iterCtx)}`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"negotiation.tools.d.ts","sourceRoot":"/","sources":["negotiation/negotiation.tools.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAY5E;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,QAAgB,CAAC;AAiCpD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,4BAg2B5E"}
1
+ {"version":3,"file":"negotiation.tools.d.ts","sourceRoot":"/","sources":["negotiation/negotiation.tools.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAY5E;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,QAAgB,CAAC;AAiCpD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,4BAg4B5E"}