@inbrowser/agent 0.0.0-placeholder → 0.1.0

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 (202) hide show
  1. package/AGENTS.md +270 -0
  2. package/README.md +117 -2
  3. package/bin/agent.ts +10 -0
  4. package/dist/cli/commands/describe.d.ts +14 -0
  5. package/dist/cli/commands/describe.d.ts.map +1 -0
  6. package/dist/cli/commands/describe.js +179 -0
  7. package/dist/cli/commands/describe.js.map +1 -0
  8. package/dist/cli/commands/events.d.ts +21 -0
  9. package/dist/cli/commands/events.d.ts.map +1 -0
  10. package/dist/cli/commands/events.js +59 -0
  11. package/dist/cli/commands/events.js.map +1 -0
  12. package/dist/cli/commands/fleet.d.ts +15 -0
  13. package/dist/cli/commands/fleet.d.ts.map +1 -0
  14. package/dist/cli/commands/fleet.js +149 -0
  15. package/dist/cli/commands/fleet.js.map +1 -0
  16. package/dist/cli/commands/help.d.ts +15 -0
  17. package/dist/cli/commands/help.d.ts.map +1 -0
  18. package/dist/cli/commands/help.js +93 -0
  19. package/dist/cli/commands/help.js.map +1 -0
  20. package/dist/cli/commands/migrate.d.ts +27 -0
  21. package/dist/cli/commands/migrate.d.ts.map +1 -0
  22. package/dist/cli/commands/migrate.js +109 -0
  23. package/dist/cli/commands/migrate.js.map +1 -0
  24. package/dist/cli/commands/run.d.ts +38 -0
  25. package/dist/cli/commands/run.d.ts.map +1 -0
  26. package/dist/cli/commands/run.js +535 -0
  27. package/dist/cli/commands/run.js.map +1 -0
  28. package/dist/cli/commands/schema.d.ts +8 -0
  29. package/dist/cli/commands/schema.d.ts.map +1 -0
  30. package/dist/cli/commands/schema.js +12 -0
  31. package/dist/cli/commands/schema.js.map +1 -0
  32. package/dist/cli/commands/serve.d.ts +39 -0
  33. package/dist/cli/commands/serve.d.ts.map +1 -0
  34. package/dist/cli/commands/serve.js +65 -0
  35. package/dist/cli/commands/serve.js.map +1 -0
  36. package/dist/cli/commands/undo.d.ts +36 -0
  37. package/dist/cli/commands/undo.d.ts.map +1 -0
  38. package/dist/cli/commands/undo.js +132 -0
  39. package/dist/cli/commands/undo.js.map +1 -0
  40. package/dist/cli/fixtures.d.ts +17 -0
  41. package/dist/cli/fixtures.d.ts.map +1 -0
  42. package/dist/cli/fixtures.js +107 -0
  43. package/dist/cli/fixtures.js.map +1 -0
  44. package/dist/cli/hardening.d.ts +39 -0
  45. package/dist/cli/hardening.d.ts.map +1 -0
  46. package/dist/cli/hardening.js +68 -0
  47. package/dist/cli/hardening.js.map +1 -0
  48. package/dist/cli/index.d.ts +28 -0
  49. package/dist/cli/index.d.ts.map +1 -0
  50. package/dist/cli/index.js +19 -0
  51. package/dist/cli/index.js.map +1 -0
  52. package/dist/cli/llm/openrouter.d.ts +33 -0
  53. package/dist/cli/llm/openrouter.d.ts.map +1 -0
  54. package/dist/cli/llm/openrouter.js +285 -0
  55. package/dist/cli/llm/openrouter.js.map +1 -0
  56. package/dist/cli/main.d.ts +32 -0
  57. package/dist/cli/main.d.ts.map +1 -0
  58. package/dist/cli/main.js +106 -0
  59. package/dist/cli/main.js.map +1 -0
  60. package/dist/cli/output.d.ts +36 -0
  61. package/dist/cli/output.d.ts.map +1 -0
  62. package/dist/cli/output.js +95 -0
  63. package/dist/cli/output.js.map +1 -0
  64. package/dist/cli/parse.d.ts +26 -0
  65. package/dist/cli/parse.d.ts.map +1 -0
  66. package/dist/cli/parse.js +160 -0
  67. package/dist/cli/parse.js.map +1 -0
  68. package/dist/cli/session-log.d.ts +34 -0
  69. package/dist/cli/session-log.d.ts.map +1 -0
  70. package/dist/cli/session-log.js +52 -0
  71. package/dist/cli/session-log.js.map +1 -0
  72. package/dist/cli/spec.d.ts +62 -0
  73. package/dist/cli/spec.d.ts.map +1 -0
  74. package/dist/cli/spec.js +510 -0
  75. package/dist/cli/spec.js.map +1 -0
  76. package/dist/cli/ui/RunView.d.ts +134 -0
  77. package/dist/cli/ui/RunView.d.ts.map +1 -0
  78. package/dist/cli/ui/RunView.js +341 -0
  79. package/dist/cli/ui/RunView.js.map +1 -0
  80. package/dist/events/codec.d.ts +79 -0
  81. package/dist/events/codec.d.ts.map +1 -0
  82. package/dist/events/codec.js +142 -0
  83. package/dist/events/codec.js.map +1 -0
  84. package/dist/events/log-core.d.ts +76 -0
  85. package/dist/events/log-core.d.ts.map +1 -0
  86. package/dist/events/log-core.js +73 -0
  87. package/dist/events/log-core.js.map +1 -0
  88. package/dist/events/log.d.ts +60 -0
  89. package/dist/events/log.d.ts.map +1 -0
  90. package/dist/events/log.js +193 -0
  91. package/dist/events/log.js.map +1 -0
  92. package/dist/events/replay.d.ts +106 -0
  93. package/dist/events/replay.d.ts.map +1 -0
  94. package/dist/events/replay.js +137 -0
  95. package/dist/events/replay.js.map +1 -0
  96. package/dist/events/wrap.d.ts +100 -0
  97. package/dist/events/wrap.d.ts.map +1 -0
  98. package/dist/events/wrap.js +141 -0
  99. package/dist/events/wrap.js.map +1 -0
  100. package/dist/index.d.ts +52 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +37 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/llm-adapter.d.ts +96 -0
  105. package/dist/llm-adapter.d.ts.map +1 -0
  106. package/dist/llm-adapter.js +132 -0
  107. package/dist/llm-adapter.js.map +1 -0
  108. package/dist/mcp/serve.d.ts +70 -0
  109. package/dist/mcp/serve.d.ts.map +1 -0
  110. package/dist/mcp/serve.js +154 -0
  111. package/dist/mcp/serve.js.map +1 -0
  112. package/dist/metrics/runs.d.ts +58 -0
  113. package/dist/metrics/runs.d.ts.map +1 -0
  114. package/dist/metrics/runs.js +99 -0
  115. package/dist/metrics/runs.js.map +1 -0
  116. package/dist/metrics.d.ts +38 -0
  117. package/dist/metrics.d.ts.map +1 -0
  118. package/dist/metrics.js +123 -0
  119. package/dist/metrics.js.map +1 -0
  120. package/dist/node.d.ts +22 -0
  121. package/dist/node.d.ts.map +1 -0
  122. package/dist/node.js +22 -0
  123. package/dist/node.js.map +1 -0
  124. package/dist/session.d.ts +10 -0
  125. package/dist/session.d.ts.map +1 -0
  126. package/dist/session.js +179 -0
  127. package/dist/session.js.map +1 -0
  128. package/dist/storage.d.ts +14 -0
  129. package/dist/storage.d.ts.map +1 -0
  130. package/dist/storage.js +58 -0
  131. package/dist/storage.js.map +1 -0
  132. package/dist/strategy.d.ts +26 -0
  133. package/dist/strategy.d.ts.map +1 -0
  134. package/dist/strategy.js +200 -0
  135. package/dist/strategy.js.map +1 -0
  136. package/dist/tools.d.ts +26 -0
  137. package/dist/tools.d.ts.map +1 -0
  138. package/dist/tools.js +129 -0
  139. package/dist/tools.js.map +1 -0
  140. package/dist/types/agent.d.ts +94 -0
  141. package/dist/types/agent.d.ts.map +1 -0
  142. package/dist/types/agent.js +17 -0
  143. package/dist/types/agent.js.map +1 -0
  144. package/dist/types/capabilities.d.ts +17 -0
  145. package/dist/types/capabilities.d.ts.map +1 -0
  146. package/dist/types/capabilities.js +13 -0
  147. package/dist/types/capabilities.js.map +1 -0
  148. package/dist/types/chat.d.ts +74 -0
  149. package/dist/types/chat.d.ts.map +1 -0
  150. package/dist/types/chat.js +10 -0
  151. package/dist/types/chat.js.map +1 -0
  152. package/dist/types/events.d.ts +115 -0
  153. package/dist/types/events.d.ts.map +1 -0
  154. package/dist/types/events.js +30 -0
  155. package/dist/types/events.js.map +1 -0
  156. package/dist/types/llm.d.ts +89 -0
  157. package/dist/types/llm.d.ts.map +1 -0
  158. package/dist/types/llm.js +12 -0
  159. package/dist/types/llm.js.map +1 -0
  160. package/dist/types/metrics.d.ts +34 -0
  161. package/dist/types/metrics.d.ts.map +1 -0
  162. package/dist/types/metrics.js +10 -0
  163. package/dist/types/metrics.js.map +1 -0
  164. package/dist/types/observer.d.ts +41 -0
  165. package/dist/types/observer.d.ts.map +1 -0
  166. package/dist/types/observer.js +41 -0
  167. package/dist/types/observer.js.map +1 -0
  168. package/dist/types/project-context.d.ts +18 -0
  169. package/dist/types/project-context.d.ts.map +1 -0
  170. package/dist/types/project-context.js +11 -0
  171. package/dist/types/project-context.js.map +1 -0
  172. package/dist/types/runtime.d.ts +71 -0
  173. package/dist/types/runtime.d.ts.map +1 -0
  174. package/dist/types/runtime.js +21 -0
  175. package/dist/types/runtime.js.map +1 -0
  176. package/dist/types/session.d.ts +103 -0
  177. package/dist/types/session.d.ts.map +1 -0
  178. package/dist/types/session.js +11 -0
  179. package/dist/types/session.js.map +1 -0
  180. package/dist/types/storage.d.ts +20 -0
  181. package/dist/types/storage.d.ts.map +1 -0
  182. package/dist/types/storage.js +41 -0
  183. package/dist/types/storage.js.map +1 -0
  184. package/dist/types/strategy.d.ts +76 -0
  185. package/dist/types/strategy.d.ts.map +1 -0
  186. package/dist/types/strategy.js +10 -0
  187. package/dist/types/strategy.js.map +1 -0
  188. package/dist/types/tools.d.ts +136 -0
  189. package/dist/types/tools.d.ts.map +1 -0
  190. package/dist/types/tools.js +11 -0
  191. package/dist/types/tools.js.map +1 -0
  192. package/dist/types/trace.d.ts +125 -0
  193. package/dist/types/trace.d.ts.map +1 -0
  194. package/dist/types/trace.js +24 -0
  195. package/dist/types/trace.js.map +1 -0
  196. package/dist/types/workspace.d.ts +29 -0
  197. package/dist/types/workspace.d.ts.map +1 -0
  198. package/dist/types/workspace.js +18 -0
  199. package/dist/types/workspace.js.map +1 -0
  200. package/package.json +45 -14
  201. package/skills/agent-cli.md +218 -0
  202. package/index.js +0 -2
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Adapter from the playground's existing `LlmProvider` shape to the
3
+ * core `LlmClient` interface. Lets new core code (AgentSession,
4
+ * AgentStrategy) consume providers via the narrow event-stream
5
+ * surface without each provider needing a refactor.
6
+ *
7
+ * The existing `LlmProvider` is callback-based (`onText`,
8
+ * `onToolCall`, etc.) and lives in the React host alongside its
9
+ * BYOK forms + localStorage wiring. This file flips it into the
10
+ * `AsyncIterable<ChatEvent>` shape the core wants.
11
+ *
12
+ * Each provider can later migrate to natively implement `LlmClient`
13
+ * to drop this adapter. The plan calls this "slice 4 — one
14
+ * provider at a time"; the adapter exists so slice 4 doesn't have
15
+ * to land atomically.
16
+ */
17
+ /**
18
+ * Wrap a legacy `LlmProvider` instance in the `LlmClient` shape.
19
+ * The adapter:
20
+ *
21
+ * - Translates `ChatRequest` → legacy `chatWithTools` / `ask`
22
+ * call.
23
+ * - Buffers callback events into an async queue and replays them
24
+ * as a `ChatEvent` `AsyncIterable`.
25
+ * - Forwards the final usage + details as a `turn_complete`
26
+ * event before closing the stream.
27
+ */
28
+ export function legacyProviderAsLlmClient(legacy, id) {
29
+ return {
30
+ id,
31
+ supportsTools: legacy.supportsTools ?? typeof legacy.chatWithTools === 'function',
32
+ chat(req, signal) {
33
+ return drive(legacy, req, signal);
34
+ },
35
+ };
36
+ }
37
+ async function* drive(legacy, req, signal) {
38
+ const queue = [];
39
+ let resolver = null;
40
+ let done = false;
41
+ function push(ev) {
42
+ queue.push(ev);
43
+ resolver?.();
44
+ resolver = null;
45
+ }
46
+ function finish() {
47
+ done = true;
48
+ resolver?.();
49
+ resolver = null;
50
+ }
51
+ const callbacks = {
52
+ onText: (chunk) => push({ kind: 'text', chunk }),
53
+ onThinking: (chunk) => push({ kind: 'thinking', chunk }),
54
+ onToolCall: (call) => push({
55
+ kind: 'tool_call',
56
+ id: call.callId,
57
+ name: call.name,
58
+ args: call.args,
59
+ signature: call.signature,
60
+ }),
61
+ signal,
62
+ };
63
+ const messagesLegacy = req.messages.map((m) => ({
64
+ role: m.role,
65
+ text: m.text,
66
+ ...(m.toolCalls ? { toolCalls: m.toolCalls } : {}),
67
+ ...(m.callId ? { callId: m.callId } : {}),
68
+ ...(m.name ? { name: m.name } : {}),
69
+ ...(m.resultJson !== undefined ? { resultJson: m.resultJson } : {}),
70
+ }));
71
+ const toolsLegacy = req.tools.map((t) => ({
72
+ name: t.name,
73
+ description: t.description,
74
+ parameters: t.parameters,
75
+ }));
76
+ let result;
77
+ let error;
78
+ const driver = (async () => {
79
+ try {
80
+ if (req.toolUseEnabled && legacy.chatWithTools) {
81
+ result = await legacy.chatWithTools(messagesLegacy, toolsLegacy, callbacks);
82
+ }
83
+ else {
84
+ // Plain-chat path — flatten messages into a single prompt.
85
+ const prompt = messagesLegacy
86
+ .filter((m) => m.role === 'user' || m.role === 'system')
87
+ .map((m) => m.text ?? '')
88
+ .filter(Boolean)
89
+ .join('\n\n');
90
+ result = await legacy.ask(prompt, callbacks.onText, { signal });
91
+ }
92
+ }
93
+ catch (e) {
94
+ error = e;
95
+ }
96
+ finally {
97
+ finish();
98
+ }
99
+ })();
100
+ while (!done || queue.length > 0) {
101
+ if (queue.length === 0) {
102
+ await new Promise((r) => {
103
+ resolver = r;
104
+ });
105
+ }
106
+ const next = queue.shift();
107
+ if (next)
108
+ yield next;
109
+ }
110
+ await driver;
111
+ if (error) {
112
+ yield { kind: 'error', message: error instanceof Error ? error.message : String(error) };
113
+ return;
114
+ }
115
+ if (result) {
116
+ const rawUsage = {
117
+ promptTokens: result.usage?.promptTokens ?? 0,
118
+ completionTokens: result.usage?.outputTokens ?? 0,
119
+ cachedTokens: result.usage?.cachedTokens,
120
+ reasoningTokens: result.usage?.reasoningTokens,
121
+ ...(typeof result.usage?.costUsd === 'number' ? { costUsd: result.usage.costUsd } : {}),
122
+ };
123
+ const details = {
124
+ requestedModel: result.details?.requestedModel ?? '',
125
+ ...(result.details?.servedModel ? { servedModel: result.details.servedModel } : {}),
126
+ ...(result.details?.fingerprint ? { fingerprint: result.details.fingerprint } : {}),
127
+ ...(result.details?.routing ? { routing: result.details.routing } : {}),
128
+ };
129
+ yield { kind: 'turn_complete', usage: rawUsage, details };
130
+ }
131
+ }
132
+ //# sourceMappingURL=llm-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-adapter.js","sourceRoot":"","sources":["../src/llm-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Inverse-mode MCP serve adapter — exposes a set of `AgentDefinition`s
3
+ * as stdio MCP tools an external host (Claude Code, Claude Desktop,
4
+ * Cursor, ...) can call.
5
+ *
6
+ * Wire model: each `AgentDefinition.tools[i]` becomes one MCP tool
7
+ * registered under its own behavior-name verbatim. The host LLM matches
8
+ * user intent against `description`; nothing about the `AgentDefinition`
9
+ * itself surfaces. (Developers see definitions; LLMs see tools.)
10
+ *
11
+ * The transport is stdio. This module deliberately writes nothing to
12
+ * stdout — the MCP transport owns that file descriptor. Startup logs
13
+ * and per-call traces go to stderr.
14
+ */
15
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
16
+ import type { EventLog } from '../events/log-core.js';
17
+ import { type RunLog } from '../metrics/runs.js';
18
+ import type { AgentDefinition, AgentTool } from '../types/agent.js';
19
+ import type { ProjectContext } from '../types/project-context.js';
20
+ export interface ServeAgentsOptions {
21
+ agents: AgentDefinition[];
22
+ /** Firebase project id — routes event log + runs log paths. */
23
+ projectId: string;
24
+ /** Override `~/.pyric/projects` for tests. */
25
+ eventsDir?: string;
26
+ /** Override the runs log directory. Defaults to `eventsDir`. */
27
+ runsDir?: string;
28
+ /** Server identity advertised in the MCP handshake. */
29
+ serverName?: string;
30
+ serverVersion?: string;
31
+ /** Injectable clock. */
32
+ now?: () => number;
33
+ /** Stream for diagnostic messages. Defaults to process.stderr. */
34
+ stderr?: NodeJS.WriteStream;
35
+ /**
36
+ * Initialized `ProjectContext`. Pass through to every
37
+ * `AgentContext` so tools that need live project access (e.g.
38
+ * `audit_firestore_backend` in `projectId` mode) can use it.
39
+ * The caller is responsible for initialization — usually via
40
+ * `toProjectContext(await initializeAgentApp())` after reading
41
+ * FIREBASE_SA_BASE64.
42
+ */
43
+ agentApp?: ProjectContext;
44
+ }
45
+ export interface ServeAgentsHandle {
46
+ /** Close the event/run logs and the MCP server. */
47
+ close(): Promise<void>;
48
+ }
49
+ /**
50
+ * Build a stdio MCP server exposing the given agents' tools. Connects
51
+ * immediately and resolves once the transport is wired. The returned
52
+ * handle lets tests shut down cleanly; in production the process
53
+ * terminates with the parent (Claude Code, etc.).
54
+ */
55
+ export declare function serveAgentsOverStdio(opts: ServeAgentsOptions): Promise<ServeAgentsHandle>;
56
+ /**
57
+ * Lower-level entrypoint — builds the MCP server + logs without
58
+ * connecting to a transport. Used by tests to drive request handlers
59
+ * directly without spawning a stdio transport.
60
+ */
61
+ export declare function buildServer(opts: ServeAgentsOptions): {
62
+ server: Server;
63
+ eventLog: EventLog;
64
+ runLog: RunLog;
65
+ toolsByName: Map<string, {
66
+ agent: AgentDefinition;
67
+ tool: AgentTool;
68
+ }>;
69
+ };
70
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/mcp/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,KAAK,MAAM,EAA6C,MAAM,oBAAoB,CAAC;AAC5F,OAAO,KAAK,EAAgB,eAAe,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IAC5B;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAiB/F;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,eAAe,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;CACvE,CAwHA"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Inverse-mode MCP serve adapter — exposes a set of `AgentDefinition`s
3
+ * as stdio MCP tools an external host (Claude Code, Claude Desktop,
4
+ * Cursor, ...) can call.
5
+ *
6
+ * Wire model: each `AgentDefinition.tools[i]` becomes one MCP tool
7
+ * registered under its own behavior-name verbatim. The host LLM matches
8
+ * user intent against `description`; nothing about the `AgentDefinition`
9
+ * itself surfaces. (Developers see definitions; LLMs see tools.)
10
+ *
11
+ * The transport is stdio. This module deliberately writes nothing to
12
+ * stdout — the MCP transport owns that file descriptor. Startup logs
13
+ * and per-call traces go to stderr.
14
+ */
15
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
16
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
18
+ import { openEventLog } from '../events/log.js';
19
+ import { generateRunId, openRunLog } from '../metrics/runs.js';
20
+ /**
21
+ * Build a stdio MCP server exposing the given agents' tools. Connects
22
+ * immediately and resolves once the transport is wired. The returned
23
+ * handle lets tests shut down cleanly; in production the process
24
+ * terminates with the parent (Claude Code, etc.).
25
+ */
26
+ export async function serveAgentsOverStdio(opts) {
27
+ const { server, eventLog, runLog } = buildServer(opts);
28
+ const transport = new StdioServerTransport();
29
+ await server.connect(transport);
30
+ writeStderr(opts.stderr, `[pyric/agents] mcp server up · project=${opts.projectId} · agents=${opts.agents
31
+ .map((a) => a.name)
32
+ .join(',')}\n`);
33
+ return {
34
+ async close() {
35
+ await server.close();
36
+ runLog.close();
37
+ eventLog.close();
38
+ },
39
+ };
40
+ }
41
+ /**
42
+ * Lower-level entrypoint — builds the MCP server + logs without
43
+ * connecting to a transport. Used by tests to drive request handlers
44
+ * directly without spawning a stdio transport.
45
+ */
46
+ export function buildServer(opts) {
47
+ if (opts.agents.length === 0) {
48
+ throw new Error('serveAgentsOverStdio: at least one AgentDefinition is required');
49
+ }
50
+ const eventLog = openEventLog({
51
+ projectId: opts.projectId,
52
+ logDir: opts.eventsDir,
53
+ now: opts.now,
54
+ });
55
+ const runLog = openRunLog({
56
+ projectId: opts.projectId,
57
+ logDir: opts.runsDir ?? opts.eventsDir,
58
+ now: opts.now,
59
+ });
60
+ const toolsByName = new Map();
61
+ for (const agent of opts.agents) {
62
+ for (const tool of agent.tools) {
63
+ if (toolsByName.has(tool.name)) {
64
+ throw new Error(`serveAgentsOverStdio: duplicate tool name "${tool.name}" across agents (registered by ${toolsByName.get(tool.name).agent.name} and ${agent.name})`);
65
+ }
66
+ toolsByName.set(tool.name, { agent, tool });
67
+ }
68
+ }
69
+ const server = new Server({
70
+ name: opts.serverName ?? 'pyric-agents',
71
+ version: opts.serverVersion ?? '0.0.0',
72
+ }, { capabilities: { tools: {} } });
73
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
74
+ return {
75
+ tools: Array.from(toolsByName.values()).map(({ tool }) => ({
76
+ name: tool.name,
77
+ description: tool.description,
78
+ inputSchema: tool.inputSchema,
79
+ })),
80
+ };
81
+ });
82
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
83
+ const { name, arguments: args } = req.params;
84
+ const entry = toolsByName.get(name);
85
+ if (!entry) {
86
+ return {
87
+ content: [
88
+ {
89
+ type: 'text',
90
+ text: JSON.stringify({ ok: false, summary: `unknown tool: ${name}` }),
91
+ },
92
+ ],
93
+ isError: true,
94
+ };
95
+ }
96
+ const runId = generateRunId(opts.now);
97
+ const ts = new Date((opts.now ?? Date.now)()).toISOString();
98
+ const startedAt = (opts.now ?? Date.now)();
99
+ const ctx = {
100
+ runId,
101
+ projectId: opts.projectId,
102
+ events: eventLog,
103
+ signal: new AbortController().signal,
104
+ now: opts.now,
105
+ ...(opts.agentApp ? { agentApp: opts.agentApp } : {}),
106
+ };
107
+ let outcome = 'ok';
108
+ let errorSummary;
109
+ let planHash;
110
+ let eventIds = [];
111
+ let payload;
112
+ try {
113
+ const result = await entry.tool.execute(args ?? {}, ctx);
114
+ outcome = result.ok ? 'ok' : 'failed';
115
+ planHash = result.planHash;
116
+ eventIds = result.eventIds ?? [];
117
+ payload = result;
118
+ if (!result.ok)
119
+ errorSummary = result.summary;
120
+ }
121
+ catch (err) {
122
+ outcome = 'failed';
123
+ errorSummary = err instanceof Error ? err.message : String(err);
124
+ payload = { ok: false, summary: errorSummary, eventIds: [] };
125
+ }
126
+ const record = {
127
+ runId,
128
+ ts,
129
+ agent: entry.agent.name,
130
+ tool: entry.tool.name,
131
+ mode: 'inverse',
132
+ outcome,
133
+ durationMs: Math.max(0, (opts.now ?? Date.now)() - startedAt),
134
+ ...(planHash ? { planHash } : {}),
135
+ eventIds,
136
+ ...(errorSummary ? { errorSummary } : {}),
137
+ };
138
+ runLog.append(record);
139
+ return {
140
+ content: [
141
+ {
142
+ type: 'text',
143
+ text: JSON.stringify(payload),
144
+ },
145
+ ],
146
+ ...(outcome === 'failed' ? { isError: true } : {}),
147
+ };
148
+ });
149
+ return { server, eventLog, runLog, toolsByName };
150
+ }
151
+ function writeStderr(stream, msg) {
152
+ (stream ?? process.stderr).write(msg);
153
+ }
154
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/mcp/serve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Per-call run record — `~/.pyric/projects/<projectId>/runs.ndjson`.
3
+ *
4
+ * Distinct from `events.ndjson` (mutation audit) and
5
+ * `<sessionId>.ndjson` (per-session event stream). Runs is a single,
6
+ * project-scoped, append-only NDJSON file that captures one record per
7
+ * agent tool invocation. The schema carries a `mode` discriminator so
8
+ * inference-mode and inverse-mode runs land in the same file for easy
9
+ * A/B comparison.
10
+ *
11
+ * Why a separate file rather than reusing the session log: a session
12
+ * log scopes to one conversation; a run record scopes to one *call*
13
+ * across modes. The host-driven (inverse) flow has no session id —
14
+ * each MCP tool call is its own run.
15
+ */
16
+ import type { EventLogIO } from '../events/log.js';
17
+ export interface RunRecord {
18
+ /** Time-prefixed base36; sortable by emission order. */
19
+ runId: string;
20
+ /** ISO-8601 timestamp. */
21
+ ts: string;
22
+ /** AgentDefinition.name, e.g. 'hello-firestore'. */
23
+ agent: string;
24
+ /** AgentTool.name, e.g. 'design_firestore_hello_schema'. */
25
+ tool: string;
26
+ /** Discriminator across the dual-mode story. */
27
+ mode: 'inference' | 'inverse';
28
+ outcome: 'ok' | 'failed';
29
+ durationMs: number;
30
+ /** Hash a preview tool returned; consumed by the commit tool. */
31
+ planHash?: string;
32
+ /** Event log ids written this call. */
33
+ eventIds: string[];
34
+ /** Truncated error message when outcome !== 'ok'. */
35
+ errorSummary?: string;
36
+ }
37
+ export type RunRecordFilter = Partial<Pick<RunRecord, 'agent' | 'tool' | 'mode' | 'outcome'>>;
38
+ export interface RunLog {
39
+ readonly path: string;
40
+ readonly projectId: string;
41
+ append(record: RunRecord): void;
42
+ read(filter?: RunRecordFilter): RunRecord[];
43
+ close(): void;
44
+ }
45
+ export interface OpenRunLogOptions {
46
+ projectId: string;
47
+ /** Defaults to `~/.pyric/projects`. */
48
+ logDir?: string;
49
+ /** Injectable IO for tests. Same shape as `events/log.ts`. */
50
+ io?: EventLogIO;
51
+ /** Injectable clock. */
52
+ now?: () => number;
53
+ }
54
+ export declare function defaultRunsLogDir(): string;
55
+ /** Time-prefixed base36 id; matches generateEventId's shape. */
56
+ export declare function generateRunId(now?: () => number): string;
57
+ export declare function openRunLog(opts: OpenRunLogOptions): RunLog;
58
+ //# sourceMappingURL=runs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../../src/metrics/runs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,IAAI,GAAG,QAAQ,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAE9F,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,EAAE,CAAC;IAC5C,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AASD,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,MAAiB,GAAG,MAAM,CAIlE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CA4D1D"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Per-call run record — `~/.pyric/projects/<projectId>/runs.ndjson`.
3
+ *
4
+ * Distinct from `events.ndjson` (mutation audit) and
5
+ * `<sessionId>.ndjson` (per-session event stream). Runs is a single,
6
+ * project-scoped, append-only NDJSON file that captures one record per
7
+ * agent tool invocation. The schema carries a `mode` discriminator so
8
+ * inference-mode and inverse-mode runs land in the same file for easy
9
+ * A/B comparison.
10
+ *
11
+ * Why a separate file rather than reusing the session log: a session
12
+ * log scopes to one conversation; a run record scopes to one *call*
13
+ * across modes. The host-driven (inverse) flow has no session id —
14
+ * each MCP tool call is its own run.
15
+ */
16
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
17
+ import { homedir } from 'node:os';
18
+ const DEFAULT_IO = {
19
+ existsSync,
20
+ mkdirSync,
21
+ appendFileSync,
22
+ readFileSync,
23
+ };
24
+ export function defaultRunsLogDir() {
25
+ return `${homedir()}/.pyric/projects`;
26
+ }
27
+ /** Time-prefixed base36 id; matches generateEventId's shape. */
28
+ export function generateRunId(now = Date.now) {
29
+ const ts = now().toString(36).padStart(9, '0');
30
+ const rand = Math.random().toString(36).slice(2, 8);
31
+ return `run-${ts}-${rand}`;
32
+ }
33
+ export function openRunLog(opts) {
34
+ if (!/^[a-zA-Z0-9_.-]+$/.test(opts.projectId)) {
35
+ throw new Error(`openRunLog: projectId ${JSON.stringify(opts.projectId)} contains disallowed characters; use [a-zA-Z0-9_.-]+`);
36
+ }
37
+ const io = opts.io ?? DEFAULT_IO;
38
+ const baseDir = opts.logDir ?? defaultRunsLogDir();
39
+ const projectDir = `${baseDir.replace(/\/$/, '')}/${opts.projectId}`;
40
+ const path = `${projectDir}/runs.ndjson`;
41
+ if (!io.existsSync(projectDir))
42
+ io.mkdirSync(projectDir, { recursive: true });
43
+ let closed = false;
44
+ function ensureOpen() {
45
+ if (closed)
46
+ throw new Error(`run log ${path} is closed`);
47
+ }
48
+ return {
49
+ path,
50
+ projectId: opts.projectId,
51
+ append(record) {
52
+ ensureOpen();
53
+ // appendFileSync mirrors the events-log atomicity story: one
54
+ // syscall per record so concurrent writers can't interleave
55
+ // mid-line on Linux + macOS.
56
+ io.appendFileSync(path, JSON.stringify(record) + '\n');
57
+ },
58
+ read(filter) {
59
+ ensureOpen();
60
+ if (!io.existsSync(path))
61
+ return [];
62
+ const raw = io.readFileSync(path, 'utf8');
63
+ if (!raw)
64
+ return [];
65
+ const out = [];
66
+ for (const line of raw.split('\n')) {
67
+ if (!line)
68
+ continue;
69
+ let parsed;
70
+ try {
71
+ parsed = JSON.parse(line);
72
+ }
73
+ catch {
74
+ continue;
75
+ }
76
+ if (filter) {
77
+ if (filter.agent && parsed.agent !== filter.agent)
78
+ continue;
79
+ if (filter.tool && parsed.tool !== filter.tool)
80
+ continue;
81
+ if (filter.mode && parsed.mode !== filter.mode)
82
+ continue;
83
+ if (filter.outcome && parsed.outcome !== filter.outcome)
84
+ continue;
85
+ }
86
+ out.push(parsed);
87
+ }
88
+ return out;
89
+ },
90
+ close() {
91
+ if (closed)
92
+ return;
93
+ closed = true;
94
+ // No file descriptor to release; close is a logical marker so
95
+ // subsequent reads/appends throw a clean error.
96
+ },
97
+ };
98
+ }
99
+ //# sourceMappingURL=runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.js","sourceRoot":"","sources":["../../src/metrics/runs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `MetricsCollector` implementation — pricing tables + cost
3
+ * derivation in one place. Pure functions; no I/O.
4
+ *
5
+ * Pricing is per-million-token USD figures. When the provider
6
+ * reports a cost (OpenRouter's `x-cost` header), the collector
7
+ * skips estimation and marks `costEstimated: false`. When the
8
+ * model isn't in the pricing table, cost is set to 0 with
9
+ * `costEstimated: true`.
10
+ *
11
+ * Update pricing rows below when providers revise rates — there's
12
+ * no API to introspect them.
13
+ */
14
+ import type { TurnMetrics } from './types/llm.js';
15
+ import type { MetricsCollector, RecordTurnInput } from './types/metrics.js';
16
+ interface PricingRow {
17
+ /** USD per million input tokens. */
18
+ input: number;
19
+ /** USD per million output tokens. */
20
+ output: number;
21
+ /** USD per million cached input tokens (when cache-hit). */
22
+ cacheRead: number;
23
+ }
24
+ /**
25
+ * Look up a pricing row. Returns undefined when the model isn't
26
+ * priced — caller skips the cost field rather than fabricating one.
27
+ */
28
+ export declare function findPricing(llmId: string, model: string): PricingRow | undefined;
29
+ /**
30
+ * Derive a `TurnMetrics` value from one turn's raw usage. Pure
31
+ * function — used by both the collector below and direct callers
32
+ * that don't need session-level aggregation.
33
+ */
34
+ export declare function computeTurnMetrics(input: RecordTurnInput): TurnMetrics;
35
+ /** Build a stateful `MetricsCollector` for one session. */
36
+ export declare function createMetricsCollector(): MetricsCollector;
37
+ export {};
38
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAiB,MAAM,oBAAoB,CAAC;AAE3F,UAAU,UAAU;IAClB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;CACnB;AAeD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAEhF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,WAAW,CA8CtE;AAED,2DAA2D;AAC3D,wBAAgB,sBAAsB,IAAI,gBAAgB,CAqCzD"}