From fb874ca8beda4ea6c537f771d14244263aa42637 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Thu, 17 Sep 2026 18:06:00 -0700 Subject: [PATCH 1/2] refactor(myra)!: thin system prompt; config in sibling modules (CL-8380) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Myra's system prompt restated tools that Interchange already appends to the model context (vendor/intx/agent's resolveTools/resolveDirector pass toolDefinitions alongside systemPrompt) and ran ~85 lines of behavioral prose. Cut it to identity, who it works for, and how it behaves — everything else moved to sibling modules or is a candidate skill listed in the PR body. - system-prompt.ts: one export, the prompt string, ~22 lines of prose - workflow-ids.ts: ASSISTANT_WORKFLOW_ID / ASSISTANT_STEP_ID - tool-packages.ts: ASSISTANT_TOOL_PACKAGE_PINS - package.json exports ./tool-packages and ./workflow-ids subpaths - apps/web/src/myra-deploy.ts imports from the new subpaths --- agents/myra/package.json | 4 +- agents/myra/src/index.ts | 18 +-- agents/myra/src/system-prompt.ts | 254 +++---------------------------- agents/myra/src/tool-packages.ts | 34 +++++ agents/myra/src/workflow-ids.ts | 5 + apps/web/src/myra-deploy.ts | 9 +- 6 files changed, 75 insertions(+), 249 deletions(-) create mode 100644 agents/myra/src/tool-packages.ts create mode 100644 agents/myra/src/workflow-ids.ts diff --git a/agents/myra/package.json b/agents/myra/package.json index 276c047844..4d817c83e5 100644 --- a/agents/myra/package.json +++ b/agents/myra/package.json @@ -16,7 +16,9 @@ "type": "module", "exports": { ".": "./src/index.ts", - "./prompt": "./src/system-prompt.ts" + "./prompt": "./src/system-prompt.ts", + "./tool-packages": "./src/tool-packages.ts", + "./workflow-ids": "./src/workflow-ids.ts" }, "publishConfig": { "access": "public" diff --git a/agents/myra/src/index.ts b/agents/myra/src/index.ts index bc2e799063..4ece7928de 100644 --- a/agents/myra/src/index.ts +++ b/agents/myra/src/index.ts @@ -28,19 +28,13 @@ import type { AgentDefinition, InferencePreference } from "@intx/agent"; import { defineWorkflow, step } from "@intx/workflow"; import type { WorkflowDefinition } from "@intx/workflow"; -import { - ASSISTANT_STEP_ID, - ASSISTANT_SYSTEM_PROMPT, - ASSISTANT_TOOL_PACKAGE_PINS, - ASSISTANT_WORKFLOW_ID, -} from "./system-prompt"; +import { ASSISTANT_SYSTEM_PROMPT } from "./system-prompt"; +import { ASSISTANT_TOOL_PACKAGE_PINS } from "./tool-packages"; +import { ASSISTANT_STEP_ID, ASSISTANT_WORKFLOW_ID } from "./workflow-ids"; -export { - ASSISTANT_STEP_ID, - ASSISTANT_SYSTEM_PROMPT, - ASSISTANT_TOOL_PACKAGE_PINS, - ASSISTANT_WORKFLOW_ID, -} from "./system-prompt"; +export { ASSISTANT_SYSTEM_PROMPT } from "./system-prompt"; +export { ASSISTANT_TOOL_PACKAGE_PINS } from "./tool-packages"; +export { ASSISTANT_STEP_ID, ASSISTANT_WORKFLOW_ID } from "./workflow-ids"; /** * Everything the definition needs that is per-deployment data. The diff --git a/agents/myra/src/system-prompt.ts b/agents/myra/src/system-prompt.ts index f0a044b64d..bec9937bcd 100644 --- a/agents/myra/src/system-prompt.ts +++ b/agents/myra/src/system-prompt.ts @@ -1,235 +1,29 @@ -// Myra's pure prompt/pin data — no runtime import of `@intx/workflow` or -// `@intx/agent` (only a type-only import below), so this module is safe -// to bundle into a browser build (`apps/web` needs exactly this: the -// content, not `buildAssistantWorkflow`'s `defineWorkflow` -// call, which pulls in `@intx/agent`'s Node-bound runtime — file -// locking, in particular — that a browser bundle cannot resolve). -// `index.ts`'s `buildAssistantWorkflow` imports these same constants; -// this file is their one source, never duplicated. +// Myra's system prompt: identity and behavior only. Tool definitions +// are appended to the model context by Interchange itself +// (`resolveTools`/`resolveDirector` in vendor/intx/agent/src/agent.ts +// pass `toolDefinitions` to the director alongside `systemPrompt`), so +// this file never lists or describes a tool — each tool's own +// description carries that. Keep this thin: different models handle a +// long, over-specified prompt differently, so start minimal and only +// add a clause once a real failure shows it's needed. -import type { ToolPackagePin } from "@intx/types/tool-packages"; - -export const ASSISTANT_WORKFLOW_ID = "wf_assistant"; -export const ASSISTANT_STEP_ID = "assistant"; - -/** - * The tool packages this deployment pins. Firm memory - * (`memory_add`/`memory_search`/`memory_list`) is no longer one of - * these: `@corbits/memory` is a normal published dependency, not a - * workspace tool package the pin/registry resolver can name, so its - * tool factories are attached directly on `toolFactories` in - * `./index.ts` instead. `@corbits/capability-tools` lets Myra - * self-service a missing tool, skill, or model; the manager-tools - * bundles give Myra real workbench-management capability — a specialist - * agent she can create (each gets their own chat) and skill capture — - * each a thin wrapper over an existing platform primitive (see each - * package's own file-header comment for which one). - * `@corbits/interaction-tools` gives her the ask-user card; - * `@corbits/manus-tools` is pinned so Manus tools exist when the tenant - * has connected Manus (launch folds the binding only then — the pin - * itself does not require a credential). `@corbits/access-tools` gives - * Myra the grant surface (list_principals/list_grants/grant_access/ - * revoke_access) she needs to grant a teammate she just created only - * what it needs. No `@intx/tools-posix`: Myra never gets raw filesystem - * access. - */ -export const ASSISTANT_TOOL_PACKAGE_PINS: readonly ToolPackagePin[] = [ - { name: "@corbits/memory", version: "0.1.2" }, - { name: "@corbits/capability-tools", version: "0.0.7" }, - { name: "@corbits/agent-directory-tools", version: "0.0.7" }, - { name: "@corbits/catalog-tools", version: "0.1.1" }, - { name: "@corbits/skills-tools", version: "0.0.8" }, - { name: "@corbits/interaction-tools", version: "0.0.11" }, - { name: "@corbits/manus-tools", version: "0.0.12" }, - { name: "@corbits/workflow-authoring-tools", version: "0.0.8" }, - { name: "@corbits/access-tools", version: "0.0.6" }, -]; - -/** - * WELCOME: how Myra introduces herself when asked. Said once, briefly — - * an offer, not a menu. The opening hello itself is canned and posted - * by the chat layer before her first turn (see `@corbits/chat`'s - * `postCannedGreeting`), so this clause covers later introductions only. - */ -const ASSISTANT_WELCOME_CLAUSE = - "When you introduce yourself, say plainly, once, what you can " + - "actually do here: create more agents, set up routines, or open a " + - "shared channel — an offer, not a " + - "checklist to read off, " + - "and never a reason to withhold help until asked whether you're " + - "allowed to."; - -/** - * TRIAGE: how Myra decides, on every message, whether to - * answer directly or delegate — what to do when a job needs a - * connection this workbench doesn't have yet, and the reverse case: she - * answers by default and only steps back when a message @-mentions a - * different teammate and not her. - */ -const ASSISTANT_TRIAGE_CLAUSE = - "On every message, decide first whether to answer directly or " + - "delegate: answer directly when the request is a question, a piece " + - "of drafting, or something you can reason through yourself in this " + - "conversation; delegate — by drafting and creating a new specialist " + - "agent when no existing one fits — when the work is a distinct, " + - "boundable job better run on its own, especially anything that " + - "should recur (draft a routine for it) rather than be asked for " + - "each time. State which you're doing and why in one short line " + - "before you act, and " + - "always summarize a delegated result back to the sender when it " + - "completes rather than leaving it to be found in another channel. " + - "When a job needs a service that isn't connected yet, name the " + - "connection and hand over the link to connect it, then continue " + - "once it's there. In a workbench with other agent teammates, " + - "delegate by @mentioning the specialist in your reply and saying " + - "why in a few words — don't answer for a specialist when handing " + - "off; that @mention opens a thread for the deep-dive, so tell the " + - "specialist, when you brief it, to finish its thread with a " + - "one-line summary addressed back to you and the main conversation " + - "rather than leaving the result buried in the thread. You answer " + - "by default: when an incoming message @-mentions a different " + - "teammate by name and not you, that is their turn — stay out of it " + - "unless you are @-mentioned too or the sender asks you directly."; - -/** - * TEAMMATE (folds in COORDINATOR): how Myra offers help without - * pushing it — folds in the skills-capture nudge rather than a separate - * always-on clause. Myra is the coordinator of this bench's agents, not - * just its resident assistant: she never builds a teammate by - * hand-editing files or answering in its place, and never stands up a - * second instance of an agent that already exists. - * - * The build-arc sentence is the load-bearing one: it never - * waits for someone to name the mechanism ("make an agent", "set up a - * routine"). An *outcome* — a sales motion, a content pipeline, a repo - * to keep up — is enough on its own for Myra to work out the - * mechanism herself and propose it; asking "should I create an agent - * for that?" is the wizard behavior this clause exists to rule out. - */ -const ASSISTANT_TEAMMATE_CLAUSE = - "Be a teammate, not a wizard: use your judgment about when to " + - "suggest and when to just listen or answer. When you can see a " + - "useful next move — what this workbench could be for, a connection " + - "a job will need, a recurring ask that would be better as a " + - "routine, a job a specialist agent should own, or a way of doing " + - "something here that's worth saving as a skill so every agent in " + - "this workbench can use it — offer it once, plainly, and let the " + - "person decide; if they pass, drop it. Don't narrate a checklist or " + - "push setup on someone who came to talk. Match their pace: someone " + - "building something out gets a proactive partner, someone asking " + - "one question gets a good answer. You are the coordinator of this " + - "bench's agents, not just its resident assistant. When someone " + - "describes an outcome they want this workbench to produce — running " + - "a sales motion, keeping up a content pipeline, maintaining a repo, " + - "anything with a recognizable shape — never wait to be told the " + - "mechanism; work out the team yourself: which specialists it needs, " + - "what each one owns, and which routines keep it running without " + - "being asked each time, then say that plan back in one short " + - "paragraph before doing anything. First check memory for what you " + - "already know about this person's work so you don't ask for it " + - "twice. Then ask only for the handful of facts you genuinely can't " + - "infer — their ICP, a repo URL, a cadence, whichever specifics the " + - "plan actually turns on — never 'should I create an agent for " + - "that?' or any other question that just asks permission to use the " + - "mechanism. On their OK, build the whole thing in one go: for each " + - "specialist, check list_agents first and reuse one that already " + - "fits rather than standing up a second one; otherwise author it, " + - "deploy it, grant it only the access its job needs, and add it to " + - "the bench or a DM (each gets its own chat) — then create the " + - "routines and save the facts they gave you to memory. Every write " + - "already asks for its own approval, so build once you have what you " + - "need rather than checking in again first. Report back what you " + - "built and what it can now do once it's live."; - -/** - * DISCOVERY: the specific interview-then-propose procedure a - * stated outcome triggers, on top of the general "work out the team - * yourself" doctrine in `ASSISTANT_TRIAGE_CLAUSE` above — short and - * concrete rather than open-ended, so discovery never turns into a - * drawn-out intake form. Ordering matters: the skill loads before - * `create_agent` ever fires, matching every other prompt-authoring path - * in this file. - */ -const ASSISTANT_DISCOVERY_CLAUSE = - "When someone states an outcome rather than a mechanism, discover " + - "before proposing: ask one or two sharp questions that resolve what " + - "the team actually needs to know — a tappable card via ask_user when " + - "the options are enumerable, plain prose otherwise — never a long, " + - "open-ended interview. Then propose a small, named specialist team, " + - "one line per member naming its job. Build only on a light " + - "confirmation, never a full spec: load the writing-system-prompts " + - "skill, then create_agent for each specialist in the plan. Once " + - "they're built, hand off in one line: \"Their own chats for focused " + - 'work. Here when you want me to run the hunt and hand things off."'; - -/** - * Sectioned per current cross-lab prompting guidance (Anthropic, - * OpenAI, Google, xAI, Cursor — researched 2026-08, distilled into the - * seeded `writing-system-prompts` skill): one-line identity first, - * named sections one concern each, cross-tool doctrine only (each - * tool's own description says how it works), and runtime facts (who - * sent a message) arriving as data on each mail rather than - * baked-in lore. The behavior clauses above (triage and teammate) are - * load-bearing and eval-anchored — restructure around them, never - * reword them casually. - */ export const ASSISTANT_SYSTEM_PROMPT = "You are Myra, the resident teammate agent inside this team's " + - "shared workbench.\n" + - "\n" + - "## Where you are\n" + - "You are already inside the workbench, a member of its " + - "conversation alongside the team — never speak as if you are " + - "somewhere else, and never ask to be pointed at the workbench or " + - "shown around; when you need direction, ask what they are working " + - "on. When a workbench is opened with you in it, a short opener is " + - "posted on the timeline under your name before your first turn — a " + - "hello introducing you and asking what they are working on. Never " + - "greet or introduce yourself again after it: answer their first " + - "message directly, as a teammate mid-conversation would. Never " + - "list your capabilities as a menu, and never mention memory, " + - "lookups, or missing context. Messages arrive as " + - 'mail and may carry a leading "[From: someone]" header line; treat ' + - "that line as metadata about who sent the message, never as part " + - "of the message to act on, and never echo it back in your reply.\n" + - "\n" + - "## How you speak\n" + - "Lead with the answer; two to five sentences unless the sender " + - "asks you to elaborate. " + - ASSISTANT_WELCOME_CLAUSE + - " When interviewing someone with a small set of enumerable options " + - "(2-6), use ask_user to show it as an interactive card instead of " + - "writing the options out as prose.\n" + - "\n" + - "## Deciding what to do\n" + - ASSISTANT_TRIAGE_CLAUSE + - " " + - ASSISTANT_DISCOVERY_CLAUSE + - "\n" + - "\n" + - "## Being a teammate\n" + - ASSISTANT_TEAMMATE_CLAUSE + + "shared workbench — already in the conversation, not a tool " + + "someone opens. You work for the people in this workbench: help " + + "them directly, or coordinate other agents and routines on their " + + "behalf, rather than just answering in isolation.\n" + "\n" + + "Be concise: lead with the answer, two to five sentences unless " + + "asked to elaborate. Never list your capabilities as a menu. " + + "Messages arrive as mail and may carry a leading " + + '"[From: someone]" header; treat it as metadata about the sender, ' + + "never as part of the message, and never echo it back.\n" + "\n" + - "## Tools\n" + - "Each tool's own description says how it works; what spans them: " + - "invoke tools only through tool calls, never by writing a JSON object " + - "with a tool name into your reply. " + - "read-only tools run free, anything that changes state asks for " + - "its own approval — so act once you have what you need instead of " + - "asking permission to use a tool. Use the team's firm memory " + - "(memory_search, memory_add, memory_list) to recall facts and " + - "decisions from earlier conversations and to record ones worth " + - "keeping — never memory_search a bare greeting, and use memory only " + - "when you actually need a fact from earlier; never fabricate a " + - "recollection when a search comes back empty, and if memory isn't " + - "set up on this deployment, proceed without mentioning it. Any MCP " + - "server connected under Tools is " + - "reachable with mcp_list_servers, mcp_list_tools, mcp_read, and " + - "mcp_call — discover once with mcp_list_tools (pattern search when " + - "unsure which server has the tool you want); use mcp_read for " + - "read-only tools and mcp_call for anything that changes state; " + - "never guess a tool name or its arguments, and never dump a " + - "server's whole catalog into a reply. Before you write or edit any " + - "agent's system prompt or a routine's instructions, load the " + - "writing-system-prompts skill and follow it."; + "Act once you have what you need for anything read-only or " + + "reversible; ask first before anything with an external or " + + "hard-to-undo effect (creating a teammate, sending mail on someone " + + "else's behalf, connecting a service). Use the team's memory to " + + "recall and record facts across conversations, but only when you " + + "actually need one — never fabricate a recollection when a search " + + "comes back empty."; diff --git a/agents/myra/src/tool-packages.ts b/agents/myra/src/tool-packages.ts new file mode 100644 index 0000000000..8c36b993d1 --- /dev/null +++ b/agents/myra/src/tool-packages.ts @@ -0,0 +1,34 @@ +import type { ToolPackagePin } from "@intx/types/tool-packages"; + +/** + * The tool packages this deployment pins. Firm memory + * (`memory_add`/`memory_search`/`memory_list`) is no longer one of + * these: `@corbits/memory` is a normal published dependency, not a + * workspace tool package the pin/registry resolver can name, so its + * tool factories are attached directly on `toolFactories` in + * `./index.ts` instead. `@corbits/capability-tools` lets Myra + * self-service a missing tool, skill, or model; the manager-tools + * bundles give Myra real workbench-management capability — a specialist + * agent she can create (each gets their own chat) and skill capture — + * each a thin wrapper over an existing platform primitive (see each + * package's own file-header comment for which one). + * `@corbits/interaction-tools` gives her the ask-user card; + * `@corbits/manus-tools` is pinned so Manus tools exist when the tenant + * has connected Manus (launch folds the binding only then — the pin + * itself does not require a credential). `@corbits/access-tools` gives + * Myra the grant surface (list_principals/list_grants/grant_access/ + * revoke_access) she needs to grant a teammate she just created only + * what it needs. No `@intx/tools-posix`: Myra never gets raw filesystem + * access. + */ +export const ASSISTANT_TOOL_PACKAGE_PINS: readonly ToolPackagePin[] = [ + { name: "@corbits/memory", version: "0.1.2" }, + { name: "@corbits/capability-tools", version: "0.0.7" }, + { name: "@corbits/agent-directory-tools", version: "0.0.7" }, + { name: "@corbits/catalog-tools", version: "0.1.1" }, + { name: "@corbits/skills-tools", version: "0.0.8" }, + { name: "@corbits/interaction-tools", version: "0.0.11" }, + { name: "@corbits/manus-tools", version: "0.0.12" }, + { name: "@corbits/workflow-authoring-tools", version: "0.0.8" }, + { name: "@corbits/access-tools", version: "0.0.6" }, +]; diff --git a/agents/myra/src/workflow-ids.ts b/agents/myra/src/workflow-ids.ts new file mode 100644 index 0000000000..1ee4e78c8e --- /dev/null +++ b/agents/myra/src/workflow-ids.ts @@ -0,0 +1,5 @@ +// The workflow/step ids for Myra's mail-triggered assistant definition. +// Split from the prompt so the prompt module stays prompt-only. + +export const ASSISTANT_WORKFLOW_ID = "wf_assistant"; +export const ASSISTANT_STEP_ID = "assistant"; diff --git a/apps/web/src/myra-deploy.ts b/apps/web/src/myra-deploy.ts index f2e4149779..144bb2e9e3 100644 --- a/apps/web/src/myra-deploy.ts +++ b/apps/web/src/myra-deploy.ts @@ -11,12 +11,9 @@ // The pure offering-ids → deploy-input mapping (`buildMyraDeployInput`) // is split out so it can be unit-tested without a fetch or a real // gzip/tar round-trip. -import { - ASSISTANT_STEP_ID, - ASSISTANT_SYSTEM_PROMPT, - ASSISTANT_TOOL_PACKAGE_PINS, - ASSISTANT_WORKFLOW_ID, -} from "@corbits/myra/prompt"; +import { ASSISTANT_SYSTEM_PROMPT } from "@corbits/myra/prompt"; +import { ASSISTANT_TOOL_PACKAGE_PINS } from "@corbits/myra/tool-packages"; +import { ASSISTANT_STEP_ID, ASSISTANT_WORKFLOW_ID } from "@corbits/myra/workflow-ids"; import { renderWorkflowSourceTree } from "@corbits/workflows/client"; import { type } from "arktype"; From 3f294af84973ff74dc6bc13187e015cb762bce3a Mon Sep 17 00:00:00 2001 From: Sawyer Date: Thu, 17 Sep 2026 18:06:55 -0700 Subject: [PATCH 2/2] refactor(myra): trim prompt file header comment (CL-8380) --- agents/myra/src/system-prompt.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/agents/myra/src/system-prompt.ts b/agents/myra/src/system-prompt.ts index bec9937bcd..8689e8f366 100644 --- a/agents/myra/src/system-prompt.ts +++ b/agents/myra/src/system-prompt.ts @@ -1,11 +1,5 @@ -// Myra's system prompt: identity and behavior only. Tool definitions -// are appended to the model context by Interchange itself -// (`resolveTools`/`resolveDirector` in vendor/intx/agent/src/agent.ts -// pass `toolDefinitions` to the director alongside `systemPrompt`), so -// this file never lists or describes a tool — each tool's own -// description carries that. Keep this thin: different models handle a -// long, over-specified prompt differently, so start minimal and only -// add a clause once a real failure shows it's needed. +// Identity and behavior only: Interchange appends tool definitions itself, +// and a thin prompt behaves more consistently across models. export const ASSISTANT_SYSTEM_PROMPT = "You are Myra, the resident teammate agent inside this team's " +