@moda-ai/cli 1.10.2 → 1.12.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 (59) hide show
  1. package/dist/agent-schemas.js +60 -3
  2. package/dist/auth.js +5 -1
  3. package/dist/cli.js +124 -38
  4. package/dist/config.js +5 -3
  5. package/dist/errors.d.ts +32 -0
  6. package/dist/errors.js +40 -0
  7. package/dist/exit-funnel.d.ts +14 -0
  8. package/dist/exit-funnel.js +49 -0
  9. package/dist/harness.js +18 -6
  10. package/dist/init/index.js +57 -4
  11. package/dist/init/sdk-guide.d.ts +28 -1
  12. package/dist/init/sdk-guide.js +102 -98
  13. package/dist/init/sdk-integration.d.ts +23 -3
  14. package/dist/init/sdk-integration.js +106 -24
  15. package/dist/init/skill-resolver.d.ts +19 -0
  16. package/dist/init/skill-resolver.js +78 -0
  17. package/dist/init/tenant.d.ts +6 -1
  18. package/dist/init/tenant.js +35 -11
  19. package/dist/manifest.d.ts +5 -0
  20. package/dist/manifest.js +111 -0
  21. package/dist/output.d.ts +44 -4
  22. package/dist/output.js +337 -32
  23. package/dist/prompts.d.ts +2 -1
  24. package/dist/prompts.js +38 -13
  25. package/dist/redact.d.ts +2 -0
  26. package/dist/redact.js +81 -0
  27. package/dist/schemas.d.ts +2 -2
  28. package/dist/skills-bundled.d.ts +35 -0
  29. package/dist/skills-bundled.js +157 -0
  30. package/dist/skills-manifest.d.ts +33 -0
  31. package/dist/skills-manifest.js +244 -0
  32. package/dist/skills.d.ts +8 -2
  33. package/dist/skills.js +55 -161
  34. package/package.json +6 -4
  35. package/skills/integration/index.json +123 -0
  36. package/skills/integration/integration-node-anthropic/EXAMPLE.md +59 -0
  37. package/skills/integration/integration-node-anthropic/SKILL.md +150 -0
  38. package/skills/integration/integration-node-generic/EXAMPLE.md +77 -0
  39. package/skills/integration/integration-node-generic/SKILL.md +170 -0
  40. package/skills/integration/integration-node-langchain/EXAMPLE.md +59 -0
  41. package/skills/integration/integration-node-langchain/SKILL.md +144 -0
  42. package/skills/integration/integration-node-openai/EXAMPLE.md +55 -0
  43. package/skills/integration/integration-node-openai/SKILL.md +148 -0
  44. package/skills/integration/integration-node-vercel-ai-sdk/EXAMPLE.md +55 -0
  45. package/skills/integration/integration-node-vercel-ai-sdk/SKILL.md +179 -0
  46. package/skills/integration/integration-node-vercel-ai-sdk/references/prompt-attribution.md +31 -0
  47. package/skills/integration/integration-python-anthropic/EXAMPLE.md +66 -0
  48. package/skills/integration/integration-python-anthropic/SKILL.md +153 -0
  49. package/skills/integration/integration-python-claude-agent-sdk/EXAMPLE.md +61 -0
  50. package/skills/integration/integration-python-claude-agent-sdk/SKILL.md +129 -0
  51. package/skills/integration/integration-python-generic/EXAMPLE.md +46 -0
  52. package/skills/integration/integration-python-generic/SKILL.md +146 -0
  53. package/skills/integration/integration-python-langchain/EXAMPLE.md +63 -0
  54. package/skills/integration/integration-python-langchain/SKILL.md +141 -0
  55. package/skills/integration/integration-python-openai/EXAMPLE.md +65 -0
  56. package/skills/integration/integration-python-openai/SKILL.md +151 -0
  57. package/skills/moda-cli/SKILL.md +51 -7
  58. package/dist/init/detect.d.ts +0 -9
  59. package/dist/init/detect.js +0 -179
@@ -0,0 +1,77 @@
1
+ # Golden reference: generic Node.js app + Moda
2
+
3
+ The complete finished integration for a small Node.js chat script whose LLM
4
+ calls go through the `openai` client (automatically instrumented), plus the
5
+ manual-tracing pattern for a provider Moda does not instrument. Match this
6
+ structure; adapt names to the target repository.
7
+
8
+ ## src/main.ts
9
+
10
+ ```typescript
11
+ import { Moda, withContext } from 'moda-ai';
12
+ import OpenAI from 'openai';
13
+
14
+ // Initialize Moda once, before any LLM client is created.
15
+ await Moda.init(process.env.MODA_API_KEY!);
16
+
17
+ const client = new OpenAI();
18
+
19
+ export async function answer(sessionId: string, userId: string, message: string) {
20
+ // Scoped context: safe under concurrent callers.
21
+ return withContext(`session_${sessionId}`, userId, async () =>
22
+ client.chat.completions.create({
23
+ model: 'gpt-4o',
24
+ messages: [{ role: 'user', content: message }],
25
+ }),
26
+ );
27
+ }
28
+
29
+ const response = await answer('demo-session', 'user-123', 'Hello!');
30
+ console.log(response.choices[0].message.content);
31
+
32
+ // Flush pending telemetry before exit.
33
+ await Moda.flush();
34
+ ```
35
+
36
+ ## Manual tracing for a non-instrumented provider
37
+
38
+ Only needed when a call site does not go through an automatically
39
+ instrumented client:
40
+
41
+ ```typescript
42
+ import { Moda } from 'moda-ai';
43
+
44
+ const messages = [{ role: 'user', content: 'Hello!' }];
45
+
46
+ const data = await Moda.withLLMCall(
47
+ { vendor: 'my-provider', type: 'chat' },
48
+ async ({ span }) => {
49
+ span.reportRequest({ model: 'my-model', messages });
50
+
51
+ const response = await fetch('https://api.my-provider.example/v1/chat', {
52
+ method: 'POST',
53
+ headers: { 'Content-Type': 'application/json' },
54
+ body: JSON.stringify({ model: 'my-model', messages }),
55
+ });
56
+ const body = await response.json();
57
+
58
+ span.reportResponse({
59
+ model: body.model,
60
+ usage: body.usage,
61
+ completions: body.choices,
62
+ });
63
+
64
+ return body;
65
+ },
66
+ );
67
+ ```
68
+
69
+ ## What changed, and what did not
70
+
71
+ - `moda-ai` added to `package.json` dependencies.
72
+ - One `Moda.init` call added at startup, before any LLM client is created.
73
+ - Call sites through instrumented clients are unchanged; only context
74
+ scoping was added around them.
75
+ - Non-instrumented provider calls are wrapped in `Moda.withLLMCall`.
76
+ - No `.env` file was created or edited; `MODA_API_KEY` is read from the
77
+ runtime environment.
@@ -0,0 +1,170 @@
1
+ ---
2
+ id: integration-node-generic
3
+ name: "Integrate the Moda SDK (Node.js / TypeScript, any provider)"
4
+ version: 1.0.0
5
+ category: integration
6
+ targets:
7
+ language: node
8
+ frameworks: []
9
+ providers: []
10
+ sdk_package: moda-ai
11
+ summary: "General Moda SDK integration for a Node.js or TypeScript app when no specific framework or provider skill matches."
12
+ ---
13
+
14
+ # Integrate the Moda SDK (Node.js / TypeScript, any provider)
15
+
16
+ This is the general Node.js integration path, used when no framework- or
17
+ provider-specific skill matches. Follow the steps below in order. Each step
18
+ has a stop condition; when you hit one, stop and report instead of guessing.
19
+ `EXAMPLE.md` in this directory shows a complete finished integration; match
20
+ its structure. For API details beyond this skill, read
21
+ https://docs.moda.dev/ingestion/moda-sdk-node (if the installed `moda-ai`
22
+ package ships docs under `node_modules/moda-ai/docs/`, prefer those; they
23
+ match the installed SDK version).
24
+
25
+ ## Safety rules (apply to every step)
26
+
27
+ - Only touch the repository you were launched in.
28
+ - Never write the `MODA_API_KEY` value (or any secret) into source files, and
29
+ never create or edit `.env` files. The key belongs in the runtime
30
+ environment or a secret manager.
31
+ - Make the smallest correct change. Match the existing code style and imports.
32
+ - Do not commit unless the user asks.
33
+ - If a step is ambiguous in this repository, stop and report rather than
34
+ guessing.
35
+
36
+ ## Step 1: Find the real LLM call sites and pick the right surface
37
+
38
+ Search the repository for LLM calls and classify them:
39
+
40
+ - Calls through the `openai` or `@anthropic-ai/sdk` client: automatically
41
+ instrumented once Moda is initialized (Step 3); no per-call changes.
42
+ OpenAI-compatible gateways (for example OpenRouter used through the
43
+ `openai` client with a custom `baseURL`) count as `openai` here.
44
+ - Calls through the Vercel AI SDK (`ai` package) or LangChain: stop — use
45
+ the dedicated wrapper-framework skill instead; never instrument the
46
+ underlying provider client when a wrapper framework is in use.
47
+ - Direct HTTP calls to some other LLM provider: plan to trace them manually
48
+ with `Moda.withLLMCall` (Step 5).
49
+
50
+ If the repository has no LLM call sites at all, stop and report.
51
+
52
+ ## Step 2: Install the SDK
53
+
54
+ Install `moda-ai` with the package manager this repository already uses
55
+ (check the lockfile: `bun.lock`/`bun.lockb` means bun, `pnpm-lock.yaml` means
56
+ pnpm, `yarn.lock` means yarn, otherwise npm):
57
+
58
+ ```bash
59
+ npm install moda-ai
60
+ ```
61
+
62
+ If `moda-ai` is already in `package.json`, skip the install.
63
+
64
+ ## Step 3: Initialize Moda once at startup
65
+
66
+ `Moda.init` must run once, at startup, before any LLM client is created. If
67
+ a `Moda.init` call already exists, verify it runs at startup and skip this
68
+ step; never add a second call.
69
+
70
+ ```typescript
71
+ import { Moda } from 'moda-ai';
72
+
73
+ await Moda.init(process.env.MODA_API_KEY!);
74
+ ```
75
+
76
+ `Moda.init` is async; await it so instrumentation is active before the first
77
+ call.
78
+
79
+ ## Step 4: Set conversation and user context
80
+
81
+ Set a stable conversation id before each LLM call so multi-turn
82
+ conversations group together, and a user id when one user can be identified.
83
+ If one user reaches the same agent through several channels, use the same
84
+ user id across channels.
85
+
86
+ For sequential code, use the global properties:
87
+
88
+ ```typescript
89
+ Moda.conversationId = `session_${sessionId}`;
90
+ Moda.userId = userId;
91
+ ```
92
+
93
+ For concurrent request handlers (any web server), use the scoped helper so
94
+ overlapping requests do not leak context into each other:
95
+
96
+ ```typescript
97
+ import { withContext } from 'moda-ai';
98
+
99
+ const response = await withContext(`session_${sessionId}`, userId, async () =>
100
+ makeLlmCall(userMessage),
101
+ );
102
+ ```
103
+
104
+ Reuse the session/thread identifier the app already has. If no session
105
+ identifier exists anywhere, the SDK falls back to hashing the first user
106
+ message plus the system prompt — acceptable only for simple chatbots that
107
+ pass full message history on every call. If this repo is an agent framework
108
+ or does not pass full history, an explicit conversation id is required; stop
109
+ and report if you cannot find a session identifier to use.
110
+
111
+ ## Step 5: Manually trace non-instrumented providers (only if found in Step 1)
112
+
113
+ For providers that are not automatically instrumented (direct HTTP calls,
114
+ custom providers), wrap the call with `Moda.withLLMCall`:
115
+
116
+ ```typescript
117
+ import { Moda } from 'moda-ai';
118
+
119
+ const result = await Moda.withLLMCall(
120
+ { vendor: 'my-provider', type: 'chat' },
121
+ async ({ span }) => {
122
+ span.reportRequest({ model: 'my-model', messages });
123
+
124
+ const response = await callProviderHttpApi(messages);
125
+
126
+ span.reportResponse({
127
+ model: response.model,
128
+ usage: response.usage,
129
+ completions: response.choices,
130
+ });
131
+
132
+ return response;
133
+ },
134
+ );
135
+ ```
136
+
137
+ Skip this step entirely when every call site goes through an automatically
138
+ instrumented client.
139
+
140
+ ## Step 6: Flush before exit
141
+
142
+ For short-lived scripts and CLIs, flush before the process exits:
143
+
144
+ ```typescript
145
+ await Moda.flush();
146
+ ```
147
+
148
+ For long-running servers, flush on shutdown:
149
+
150
+ ```typescript
151
+ process.on('SIGTERM', async () => {
152
+ await Moda.flush();
153
+ await Moda.shutdown();
154
+ process.exit(0);
155
+ });
156
+ ```
157
+
158
+ ## Step 7: Validate
159
+
160
+ Report the packages installed, files edited, and any step a human still needs
161
+ to do (for example setting `MODA_API_KEY` in the deploy environment). Then
162
+ verify telemetry reaches Moda:
163
+
164
+ ```bash
165
+ moda doctor --online --json
166
+ ```
167
+
168
+ The report should show recent traces. If it does not, confirm `MODA_API_KEY`
169
+ is available in the runtime environment and that `Moda.init()` runs before
170
+ the first LLM client is created.
@@ -0,0 +1,59 @@
1
+ # Golden reference: Node.js LangChain agent + Moda
2
+
3
+ The complete finished integration for a minimal LangChain app. Match this
4
+ structure; adapt names to the target repository. Note what is absent: no
5
+ LangChain callbacks or tracer classes for Moda — instrumentation happens at
6
+ the Moda SDK level, and the only per-run change is the explicit conversation
7
+ id wrapper.
8
+
9
+ ## src/agent.ts
10
+
11
+ ```typescript
12
+ import { Moda, withContext } from 'moda-ai';
13
+ import { ChatOpenAI } from '@langchain/openai';
14
+ import { ChatPromptTemplate } from '@langchain/core/prompts';
15
+
16
+ // Initialize Moda once, before LangChain constructs any provider client,
17
+ // so instrumentation is active for everything the agent does.
18
+ await Moda.init(process.env.MODA_API_KEY!);
19
+
20
+ const model = new ChatOpenAI({ model: 'gpt-4o' });
21
+ const prompt = ChatPromptTemplate.fromMessages([
22
+ ['system', 'You are a helpful assistant.'],
23
+ ['human', '{input}'],
24
+ ]);
25
+ const chain = prompt.pipe(model);
26
+
27
+ export async function runAgent(sessionId: string, userId: string, input: string) {
28
+ // Explicit conversation id around the whole run: every internal LLM call
29
+ // made during this invocation shares it. Automatic detection does NOT
30
+ // work for agent frameworks.
31
+ return withContext(`agent_session_${sessionId}`, userId, async () =>
32
+ chain.invoke({ input }),
33
+ );
34
+ }
35
+ ```
36
+
37
+ ## src/main.ts
38
+
39
+ ```typescript
40
+ import { Moda } from 'moda-ai';
41
+ import { runAgent } from './agent.js';
42
+
43
+ const result = await runAgent('demo-session', 'user-123', 'What is TypeScript?');
44
+ console.log(result.content);
45
+
46
+ // Flush pending telemetry before exit.
47
+ await Moda.flush();
48
+ ```
49
+
50
+ ## What changed, and what did not
51
+
52
+ - `moda-ai` added to `package.json` dependencies.
53
+ - One `Moda.init` call added at startup, before any LangChain model is
54
+ constructed.
55
+ - Each agent/chain entry point is wrapped in `withContext` with an explicit
56
+ conversation id; the LangChain code itself is unchanged.
57
+ - No LangChain callbacks, handlers, or tracers were added for Moda.
58
+ - No `.env` file was created or edited; `MODA_API_KEY` is read from the
59
+ runtime environment.
@@ -0,0 +1,144 @@
1
+ ---
2
+ id: integration-node-langchain
3
+ name: "Integrate Moda with LangChain / LangGraph (Node.js / TypeScript)"
4
+ version: 1.0.0
5
+ category: integration
6
+ targets:
7
+ language: node
8
+ frameworks:
9
+ - langchain
10
+ providers: []
11
+ sdk_package: moda-ai
12
+ summary: "Instrument a Node.js LangChain or LangGraph app with Moda: init before the framework, explicit conversation ids around every run."
13
+ ---
14
+
15
+ # Integrate Moda with LangChain / LangGraph (Node.js / TypeScript)
16
+
17
+ Follow the steps below in order. Each step has a stop condition; when you hit
18
+ one, stop and report instead of guessing. `EXAMPLE.md` in this directory shows
19
+ the complete finished integration; match its structure. For API details beyond
20
+ this skill, read https://docs.moda.dev/ingestion/moda-sdk-node (if the
21
+ installed `moda-ai` package ships docs under `node_modules/moda-ai/docs/`,
22
+ prefer those; they match the installed SDK version).
23
+
24
+ ## Safety rules (apply to every step)
25
+
26
+ - Only touch the repository you were launched in.
27
+ - Never write the `MODA_API_KEY` value (or any secret) into source files, and
28
+ never create or edit `.env` files. The key belongs in the runtime
29
+ environment or a secret manager.
30
+ - Make the smallest correct change. Match the existing code style and imports.
31
+ - Do not commit unless the user asks.
32
+ - If a step is ambiguous in this repository, stop and report rather than
33
+ guessing.
34
+
35
+ ## What is different about agent frameworks
36
+
37
+ Two constraints drive every step below:
38
+
39
+ 1. Instrument at the Moda SDK level, not inside LangChain. Initialize Moda
40
+ once and set an explicit conversation id around each run; the LLM calls
41
+ the agent makes internally are then captured and share that id (this is
42
+ the documented agent-framework pattern — see the "Solution for Agent
43
+ Applications" section of the Node SDK docs). Do not add LangChain
44
+ callbacks, handlers, or tracer classes for Moda — no such Moda API
45
+ exists.
46
+ 2. Explicit conversation ids are mandatory. Moda's automatic conversation
47
+ detection hashes the first user message, and agent frameworks construct
48
+ different messages on every internal iteration, so without an explicit id
49
+ every iteration lands in a different conversation.
50
+
51
+ ## Step 1: Find the agent/chain entry points
52
+
53
+ Find where the application invokes LangChain or LangGraph: `.invoke(...)`,
54
+ `.stream(...)`, agent executor runs, or graph runs. These entry points — not
55
+ the internal LLM calls — are where conversation context gets set.
56
+
57
+ - If you cannot find any LangChain/LangGraph entry point, stop and report.
58
+ - If the repository's LLM calls actually go through the Vercel AI SDK (the
59
+ `ai` package) rather than LangChain, stop: use the Vercel AI SDK skill
60
+ instead.
61
+
62
+ ## Step 2: Install the SDK
63
+
64
+ Install `moda-ai` with the package manager this repository already uses
65
+ (check the lockfile: `bun.lock`/`bun.lockb` means bun, `pnpm-lock.yaml` means
66
+ pnpm, `yarn.lock` means yarn, otherwise npm):
67
+
68
+ ```bash
69
+ npm install moda-ai
70
+ ```
71
+
72
+ If `moda-ai` is already in `package.json`, skip the install.
73
+
74
+ ## Step 3: Initialize Moda once at startup
75
+
76
+ `Moda.init` must run before LangChain constructs any provider client (which
77
+ usually happens at model/agent construction). Initialize at the top of the
78
+ application entry point. If a `Moda.init` call already exists, verify it runs
79
+ at startup and skip this step; never add a second call.
80
+
81
+ ```typescript
82
+ import { Moda } from 'moda-ai';
83
+
84
+ await Moda.init(process.env.MODA_API_KEY!);
85
+ ```
86
+
87
+ ## Step 4: Wrap every agent/chain run in an explicit conversation id
88
+
89
+ Wrap each entry point found in Step 1 so all internal LLM calls made during
90
+ that run share one conversation id. Use the scoped helpers so concurrent
91
+ runs do not leak context into each other:
92
+
93
+ ```typescript
94
+ import { withContext } from 'moda-ai';
95
+
96
+ const result = await withContext(`agent_session_${sessionId}`, userId, async () =>
97
+ agent.invoke({ input: userQuery }),
98
+ );
99
+ ```
100
+
101
+ For strictly sequential code, the global property form is equivalent:
102
+
103
+ ```typescript
104
+ Moda.conversationId = `agent_session_${sessionId}`;
105
+ const result = await agent.invoke({ input: userQuery });
106
+ Moda.conversationId = null;
107
+ ```
108
+
109
+ Reuse the session/thread identifier the app already has (LangGraph
110
+ `thread_id`, a chat session id, a ticket id). If no session identifier
111
+ exists anywhere, stop and report; do not invent one.
112
+
113
+ ## Step 5: Flush before exit
114
+
115
+ For short-lived scripts and CLIs, flush before the process exits:
116
+
117
+ ```typescript
118
+ await Moda.flush();
119
+ ```
120
+
121
+ For long-running servers, flush on shutdown:
122
+
123
+ ```typescript
124
+ process.on('SIGTERM', async () => {
125
+ await Moda.flush();
126
+ await Moda.shutdown();
127
+ process.exit(0);
128
+ });
129
+ ```
130
+
131
+ ## Step 6: Validate
132
+
133
+ Report the packages installed, files edited, and any step a human still needs
134
+ to do (for example setting `MODA_API_KEY` in the deploy environment). Then
135
+ verify telemetry reaches Moda:
136
+
137
+ ```bash
138
+ moda doctor --online --json
139
+ ```
140
+
141
+ The report should show recent traces, and multi-step agent runs should share
142
+ one conversation id. If each internal LLM call shows up as its own
143
+ conversation, the explicit conversation id from Step 4 is not wrapping the
144
+ run — fix that before finishing.
@@ -0,0 +1,55 @@
1
+ # Golden reference: Express + OpenAI SDK + Moda
2
+
3
+ The complete finished integration for a minimal Express chat server that
4
+ calls the OpenAI SDK directly. Match this structure; adapt names to the
5
+ target repository.
6
+
7
+ ## src/server.ts
8
+
9
+ ```typescript
10
+ import express from 'express';
11
+ import { Moda, withContext } from 'moda-ai';
12
+ import OpenAI from 'openai';
13
+
14
+ // Initialize Moda once, before the OpenAI client is created. After this,
15
+ // every chat.completions.create call is captured automatically.
16
+ await Moda.init(process.env.MODA_API_KEY!);
17
+
18
+ const client = new OpenAI();
19
+ const app = express();
20
+ app.use(express.json());
21
+
22
+ app.post('/chat', async (req, res) => {
23
+ const { sessionId, userId, message } = req.body;
24
+
25
+ // Scoped context: safe under concurrent requests.
26
+ const response = await withContext(`session_${sessionId}`, userId, async () =>
27
+ client.chat.completions.create({
28
+ model: 'gpt-4o',
29
+ messages: [{ role: 'user', content: message }],
30
+ }),
31
+ );
32
+
33
+ res.json({ reply: response.choices[0].message.content });
34
+ });
35
+
36
+ // Flush pending telemetry on shutdown.
37
+ process.on('SIGTERM', async () => {
38
+ await Moda.flush();
39
+ await Moda.shutdown();
40
+ process.exit(0);
41
+ });
42
+
43
+ app.listen(3000);
44
+ ```
45
+
46
+ ## What changed, and what did not
47
+
48
+ - `moda-ai` added to `package.json` dependencies.
49
+ - One `Moda.init` call added at startup, before `new OpenAI()`.
50
+ - The handler wraps its existing OpenAI call in `withContext`; the
51
+ `chat.completions.create` call itself is unchanged — instrumentation is
52
+ automatic.
53
+ - A `SIGTERM` handler flushes telemetry on shutdown.
54
+ - No `.env` file was created or edited; `MODA_API_KEY` is read from the
55
+ runtime environment.
@@ -0,0 +1,148 @@
1
+ ---
2
+ id: integration-node-openai
3
+ name: "Integrate Moda with the OpenAI SDK (Node.js / TypeScript)"
4
+ version: 1.0.0
5
+ category: integration
6
+ targets:
7
+ language: node
8
+ frameworks: []
9
+ providers:
10
+ - openai
11
+ sdk_package: moda-ai
12
+ summary: "Auto-instrument direct openai client calls with Moda in a Node.js or TypeScript app; no per-call changes required."
13
+ ---
14
+
15
+ # Integrate Moda with the OpenAI SDK (Node.js / TypeScript)
16
+
17
+ Follow the steps below in order. Each step has a stop condition; when you hit
18
+ one, stop and report instead of guessing. `EXAMPLE.md` in this directory shows
19
+ the complete finished integration; match its structure. For API details beyond
20
+ this skill, read https://docs.moda.dev/ingestion/providers/openai and
21
+ https://docs.moda.dev/ingestion/moda-sdk-node (if the installed `moda-ai`
22
+ package ships docs under `node_modules/moda-ai/docs/`, prefer those; they
23
+ match the installed SDK version).
24
+
25
+ ## Safety rules (apply to every step)
26
+
27
+ - Only touch the repository you were launched in.
28
+ - Never write the `MODA_API_KEY` value (or any secret) into source files, and
29
+ never create or edit `.env` files. The key belongs in the runtime
30
+ environment or a secret manager.
31
+ - Make the smallest correct change. Match the existing code style and imports.
32
+ - Do not commit unless the user asks.
33
+ - If a step is ambiguous in this repository, stop and report rather than
34
+ guessing.
35
+
36
+ ## Step 1: Confirm the raw OpenAI client is the integration surface
37
+
38
+ Find the real LLM call sites: `client.chat.completions.create(...)` on a
39
+ client constructed with `new OpenAI()` from the `openai` package.
40
+
41
+ - If the OpenAI calls go through a wrapper framework instead — the `ai`
42
+ package (Vercel AI SDK) or LangChain — stop: instrument the wrapper layer
43
+ with its dedicated skill, never the underlying provider client.
44
+ - If the repo wraps its OpenAI client in a helper module (for example
45
+ `lib/openai.ts` exporting a shared client), plan to instrument at that one
46
+ place; do not edit every call site.
47
+ - If there are no OpenAI call sites at all, stop and report.
48
+
49
+ ## Step 2: Install the SDK
50
+
51
+ Install `moda-ai` with the package manager this repository already uses
52
+ (check the lockfile: `bun.lock`/`bun.lockb` means bun, `pnpm-lock.yaml` means
53
+ pnpm, `yarn.lock` means yarn, otherwise npm):
54
+
55
+ ```bash
56
+ npm install moda-ai
57
+ ```
58
+
59
+ If `moda-ai` is already in `package.json`, skip the install.
60
+
61
+ ## Step 3: Initialize Moda once at startup, before the client is created
62
+
63
+ Instrumentation is automatic: once `Moda.init` has run, every
64
+ `chat.completions.create` call (including streaming, function calling, and
65
+ embeddings) is captured with no per-call changes.
66
+
67
+ `Moda.init` must run before the OpenAI client is constructed. If a
68
+ `Moda.init` call already exists, verify it runs at startup before client
69
+ creation and skip this step; never add a second call.
70
+
71
+ ```typescript
72
+ import { Moda } from 'moda-ai';
73
+ import OpenAI from 'openai';
74
+
75
+ await Moda.init(process.env.MODA_API_KEY!);
76
+
77
+ const client = new OpenAI();
78
+ ```
79
+
80
+ If the client lives in a shared helper module, initialize Moda at the top of
81
+ the application entry point (before that module is imported), or at the top
82
+ of the helper module itself — whichever the existing structure makes
83
+ smallest.
84
+
85
+ ## Step 4: Set conversation and user context
86
+
87
+ Set a stable conversation id before each LLM call so multi-turn
88
+ conversations group together, and a user id when one user can be identified.
89
+
90
+ For sequential code, use the global properties:
91
+
92
+ ```typescript
93
+ Moda.conversationId = `session_${sessionId}`;
94
+ Moda.userId = userId;
95
+ ```
96
+
97
+ For concurrent request handlers (any web server), use the scoped helper so
98
+ overlapping requests do not leak context into each other:
99
+
100
+ ```typescript
101
+ import { withContext } from 'moda-ai';
102
+
103
+ const response = await withContext(`session_${sessionId}`, userId, async () =>
104
+ client.chat.completions.create({
105
+ model: 'gpt-4o',
106
+ messages: [{ role: 'user', content: userMessage }],
107
+ }),
108
+ );
109
+ ```
110
+
111
+ Reuse the session/thread identifier the app already has. If no session
112
+ identifier exists anywhere, the SDK falls back to hashing the first user
113
+ message plus the system prompt — acceptable only for simple chatbots that
114
+ pass full message history on every call. If this repo is an agent framework
115
+ or does not pass full history, an explicit conversation id is required; stop
116
+ and report if you cannot find a session identifier to use.
117
+
118
+ ## Step 5: Flush before exit
119
+
120
+ For short-lived scripts and CLIs, flush before the process exits:
121
+
122
+ ```typescript
123
+ await Moda.flush();
124
+ ```
125
+
126
+ For long-running servers, flush on shutdown:
127
+
128
+ ```typescript
129
+ process.on('SIGTERM', async () => {
130
+ await Moda.flush();
131
+ await Moda.shutdown();
132
+ process.exit(0);
133
+ });
134
+ ```
135
+
136
+ ## Step 6: Validate
137
+
138
+ Report the packages installed, files edited, and any step a human still needs
139
+ to do (for example setting `MODA_API_KEY` in the deploy environment). Then
140
+ verify telemetry reaches Moda:
141
+
142
+ ```bash
143
+ moda doctor --online --json
144
+ ```
145
+
146
+ The report should show recent traces. If it does not, confirm `MODA_API_KEY`
147
+ is available in the runtime environment and that `Moda.init()` runs before
148
+ the OpenAI client is created.