@moda-ai/cli 1.11.0 → 1.12.2

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 (42) hide show
  1. package/dist/cli.js +5 -0
  2. package/dist/harness-remote.d.ts +33 -0
  3. package/dist/harness-remote.js +203 -0
  4. package/dist/harness.d.ts +75 -0
  5. package/dist/harness.js +92 -37
  6. package/dist/init/index.js +14 -4
  7. package/dist/init/sdk-guide.d.ts +28 -1
  8. package/dist/init/sdk-guide.js +102 -98
  9. package/dist/init/sdk-integration.d.ts +23 -3
  10. package/dist/init/sdk-integration.js +106 -24
  11. package/dist/init/skill-resolver.d.ts +19 -0
  12. package/dist/init/skill-resolver.js +78 -0
  13. package/dist/skills-bundled.d.ts +35 -0
  14. package/dist/skills-bundled.js +157 -0
  15. package/dist/skills-manifest.d.ts +33 -0
  16. package/dist/skills-manifest.js +244 -0
  17. package/dist/skills.js +22 -142
  18. package/package.json +6 -4
  19. package/skills/integration/index.json +123 -0
  20. package/skills/integration/integration-node-anthropic/EXAMPLE.md +59 -0
  21. package/skills/integration/integration-node-anthropic/SKILL.md +150 -0
  22. package/skills/integration/integration-node-generic/EXAMPLE.md +77 -0
  23. package/skills/integration/integration-node-generic/SKILL.md +170 -0
  24. package/skills/integration/integration-node-langchain/EXAMPLE.md +59 -0
  25. package/skills/integration/integration-node-langchain/SKILL.md +144 -0
  26. package/skills/integration/integration-node-openai/EXAMPLE.md +55 -0
  27. package/skills/integration/integration-node-openai/SKILL.md +148 -0
  28. package/skills/integration/integration-node-vercel-ai-sdk/EXAMPLE.md +55 -0
  29. package/skills/integration/integration-node-vercel-ai-sdk/SKILL.md +179 -0
  30. package/skills/integration/integration-node-vercel-ai-sdk/references/prompt-attribution.md +31 -0
  31. package/skills/integration/integration-python-anthropic/EXAMPLE.md +66 -0
  32. package/skills/integration/integration-python-anthropic/SKILL.md +153 -0
  33. package/skills/integration/integration-python-claude-agent-sdk/EXAMPLE.md +61 -0
  34. package/skills/integration/integration-python-claude-agent-sdk/SKILL.md +129 -0
  35. package/skills/integration/integration-python-generic/EXAMPLE.md +46 -0
  36. package/skills/integration/integration-python-generic/SKILL.md +146 -0
  37. package/skills/integration/integration-python-langchain/EXAMPLE.md +63 -0
  38. package/skills/integration/integration-python-langchain/SKILL.md +141 -0
  39. package/skills/integration/integration-python-openai/EXAMPLE.md +65 -0
  40. package/skills/integration/integration-python-openai/SKILL.md +151 -0
  41. package/dist/init/detect.d.ts +0 -9
  42. package/dist/init/detect.js +0 -179
@@ -0,0 +1,123 @@
1
+ {
2
+ "schema_version": "moda.skill_index.v1",
3
+ "bundled_at": "2026-07-06T03:50:30.586Z",
4
+ "cli_version": "1.12.2",
5
+ "skills": [
6
+ {
7
+ "id": "integration-node-anthropic",
8
+ "version": "1.0.0",
9
+ "summary": "Auto-instrument @anthropic-ai/sdk client calls with Moda in a Node.js or TypeScript app; no per-call changes required.",
10
+ "targets": {
11
+ "language": "node",
12
+ "frameworks": [],
13
+ "providers": [
14
+ "anthropic"
15
+ ]
16
+ }
17
+ },
18
+ {
19
+ "id": "integration-node-generic",
20
+ "version": "1.0.0",
21
+ "summary": "General Moda SDK integration for a Node.js or TypeScript app when no specific framework or provider skill matches.",
22
+ "targets": {
23
+ "language": "node",
24
+ "frameworks": [],
25
+ "providers": []
26
+ }
27
+ },
28
+ {
29
+ "id": "integration-node-langchain",
30
+ "version": "1.0.0",
31
+ "summary": "Instrument a Node.js LangChain or LangGraph app with Moda: init before the framework, explicit conversation ids around every run.",
32
+ "targets": {
33
+ "language": "node",
34
+ "frameworks": [
35
+ "langchain"
36
+ ],
37
+ "providers": []
38
+ }
39
+ },
40
+ {
41
+ "id": "integration-node-openai",
42
+ "version": "1.0.0",
43
+ "summary": "Auto-instrument direct openai client calls with Moda in a Node.js or TypeScript app; no per-call changes required.",
44
+ "targets": {
45
+ "language": "node",
46
+ "frameworks": [],
47
+ "providers": [
48
+ "openai"
49
+ ]
50
+ }
51
+ },
52
+ {
53
+ "id": "integration-node-vercel-ai-sdk",
54
+ "version": "1.0.0",
55
+ "summary": "Instrument Vercel AI SDK calls (generateText, streamText, generateObject) with Moda telemetry in a Node.js or TypeScript app.",
56
+ "targets": {
57
+ "language": "node",
58
+ "frameworks": [
59
+ "vercel-ai-sdk"
60
+ ],
61
+ "providers": []
62
+ }
63
+ },
64
+ {
65
+ "id": "integration-python-anthropic",
66
+ "version": "1.0.0",
67
+ "summary": "Auto-instrument anthropic client calls with Moda in a Python app; no per-call changes required.",
68
+ "targets": {
69
+ "language": "python",
70
+ "frameworks": [],
71
+ "providers": [
72
+ "anthropic"
73
+ ]
74
+ }
75
+ },
76
+ {
77
+ "id": "integration-python-claude-agent-sdk",
78
+ "version": "1.0.0",
79
+ "summary": "Instrument Claude Agent SDK sessions (ClaudeSDKClient) with Moda via the dedicated moda-claude-agent-sdk instrumentor.",
80
+ "targets": {
81
+ "language": "python",
82
+ "frameworks": [
83
+ "claude-agent-sdk"
84
+ ],
85
+ "providers": []
86
+ }
87
+ },
88
+ {
89
+ "id": "integration-python-generic",
90
+ "version": "1.0.0",
91
+ "summary": "General Moda SDK integration for a Python app when no specific framework or provider skill matches.",
92
+ "targets": {
93
+ "language": "python",
94
+ "frameworks": [],
95
+ "providers": []
96
+ }
97
+ },
98
+ {
99
+ "id": "integration-python-langchain",
100
+ "version": "1.0.0",
101
+ "summary": "Instrument a Python LangChain or LangGraph app with Moda: init before the framework, explicit conversation ids around every run.",
102
+ "targets": {
103
+ "language": "python",
104
+ "frameworks": [
105
+ "langchain"
106
+ ],
107
+ "providers": []
108
+ }
109
+ },
110
+ {
111
+ "id": "integration-python-openai",
112
+ "version": "1.0.0",
113
+ "summary": "Auto-instrument openai client calls with Moda in a Python app; no per-call changes required.",
114
+ "targets": {
115
+ "language": "python",
116
+ "frameworks": [],
117
+ "providers": [
118
+ "openai"
119
+ ]
120
+ }
121
+ }
122
+ ]
123
+ }
@@ -0,0 +1,59 @@
1
+ # Golden reference: Express + Anthropic SDK + Moda
2
+
3
+ The complete finished integration for a minimal Express chat server that
4
+ calls the Anthropic 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 Anthropic from '@anthropic-ai/sdk';
13
+
14
+ // Initialize Moda once, before the Anthropic client is created. After this,
15
+ // every messages.create / messages.stream call is captured automatically.
16
+ await Moda.init(process.env.MODA_API_KEY!);
17
+
18
+ const client = new Anthropic();
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.messages.create({
28
+ model: 'claude-sonnet-4-20250514',
29
+ max_tokens: 1024,
30
+ messages: [{ role: 'user', content: message }],
31
+ }),
32
+ );
33
+
34
+ const reply = response.content
35
+ .filter((block) => block.type === 'text')
36
+ .map((block) => block.text)
37
+ .join('');
38
+ res.json({ reply });
39
+ });
40
+
41
+ // Flush pending telemetry on shutdown.
42
+ process.on('SIGTERM', async () => {
43
+ await Moda.flush();
44
+ await Moda.shutdown();
45
+ process.exit(0);
46
+ });
47
+
48
+ app.listen(3000);
49
+ ```
50
+
51
+ ## What changed, and what did not
52
+
53
+ - `moda-ai` added to `package.json` dependencies.
54
+ - One `Moda.init` call added at startup, before `new Anthropic()`.
55
+ - The handler wraps its existing Anthropic call in `withContext`; the
56
+ `messages.create` call itself is unchanged — instrumentation is automatic.
57
+ - A `SIGTERM` handler flushes telemetry on shutdown.
58
+ - No `.env` file was created or edited; `MODA_API_KEY` is read from the
59
+ runtime environment.
@@ -0,0 +1,150 @@
1
+ ---
2
+ id: integration-node-anthropic
3
+ name: "Integrate Moda with the Anthropic SDK (Node.js / TypeScript)"
4
+ version: 1.0.0
5
+ category: integration
6
+ targets:
7
+ language: node
8
+ frameworks: []
9
+ providers:
10
+ - anthropic
11
+ sdk_package: moda-ai
12
+ summary: "Auto-instrument @anthropic-ai/sdk client calls with Moda in a Node.js or TypeScript app; no per-call changes required."
13
+ ---
14
+
15
+ # Integrate Moda with the Anthropic 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/anthropic 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 Anthropic client is the integration surface
37
+
38
+ Find the real LLM call sites: `client.messages.create(...)` or
39
+ `client.messages.stream(...)` on a client constructed with `new Anthropic()`
40
+ from the `@anthropic-ai/sdk` package.
41
+
42
+ - If the Anthropic calls go through a wrapper framework instead — the `ai`
43
+ package (Vercel AI SDK) or LangChain — stop: instrument the wrapper layer
44
+ with its dedicated skill, never the underlying provider client.
45
+ - If the repo wraps its Anthropic client in a helper module (for example
46
+ `lib/anthropic.ts` exporting a shared client), plan to instrument at that
47
+ one place; do not edit every call site.
48
+ - If there are no Anthropic call sites at all, stop and report.
49
+
50
+ ## Step 2: Install the SDK
51
+
52
+ Install `moda-ai` with the package manager this repository already uses
53
+ (check the lockfile: `bun.lock`/`bun.lockb` means bun, `pnpm-lock.yaml` means
54
+ pnpm, `yarn.lock` means yarn, otherwise npm):
55
+
56
+ ```bash
57
+ npm install moda-ai
58
+ ```
59
+
60
+ If `moda-ai` is already in `package.json`, skip the install.
61
+
62
+ ## Step 3: Initialize Moda once at startup, before the client is created
63
+
64
+ Instrumentation is automatic: once `Moda.init` has run, every
65
+ `messages.create` / `messages.stream` call (including extended thinking and
66
+ tool use) is captured with no per-call changes.
67
+
68
+ `Moda.init` must run before the Anthropic client is constructed. If a
69
+ `Moda.init` call already exists, verify it runs at startup before client
70
+ creation and skip this step; never add a second call.
71
+
72
+ ```typescript
73
+ import { Moda } from 'moda-ai';
74
+ import Anthropic from '@anthropic-ai/sdk';
75
+
76
+ await Moda.init(process.env.MODA_API_KEY!);
77
+
78
+ const client = new Anthropic();
79
+ ```
80
+
81
+ If the client lives in a shared helper module, initialize Moda at the top of
82
+ the application entry point (before that module is imported), or at the top
83
+ of the helper module itself — whichever the existing structure makes
84
+ smallest.
85
+
86
+ ## Step 4: Set conversation and user context
87
+
88
+ Set a stable conversation id before each LLM call so multi-turn
89
+ conversations group together, and a user id when one user can be identified.
90
+
91
+ For sequential code, use the global properties:
92
+
93
+ ```typescript
94
+ Moda.conversationId = `session_${sessionId}`;
95
+ Moda.userId = userId;
96
+ ```
97
+
98
+ For concurrent request handlers (any web server), use the scoped helper so
99
+ overlapping requests do not leak context into each other:
100
+
101
+ ```typescript
102
+ import { withContext } from 'moda-ai';
103
+
104
+ const response = await withContext(`session_${sessionId}`, userId, async () =>
105
+ client.messages.create({
106
+ model: 'claude-sonnet-4-20250514',
107
+ max_tokens: 1024,
108
+ messages: [{ role: 'user', content: userMessage }],
109
+ }),
110
+ );
111
+ ```
112
+
113
+ Reuse the session/thread identifier the app already has. If no session
114
+ identifier exists anywhere, the SDK falls back to hashing the first user
115
+ message plus the system prompt — acceptable only for simple chatbots that
116
+ pass full message history on every call. If this repo is an agent framework
117
+ or does not pass full history, an explicit conversation id is required; stop
118
+ and report if you cannot find a session identifier to use.
119
+
120
+ ## Step 5: Flush before exit
121
+
122
+ For short-lived scripts and CLIs, flush before the process exits:
123
+
124
+ ```typescript
125
+ await Moda.flush();
126
+ ```
127
+
128
+ For long-running servers, flush on shutdown:
129
+
130
+ ```typescript
131
+ process.on('SIGTERM', async () => {
132
+ await Moda.flush();
133
+ await Moda.shutdown();
134
+ process.exit(0);
135
+ });
136
+ ```
137
+
138
+ ## Step 6: Validate
139
+
140
+ Report the packages installed, files edited, and any step a human still needs
141
+ to do (for example setting `MODA_API_KEY` in the deploy environment). Then
142
+ verify telemetry reaches Moda:
143
+
144
+ ```bash
145
+ moda doctor --online --json
146
+ ```
147
+
148
+ The report should show recent traces. If it does not, confirm `MODA_API_KEY`
149
+ is available in the runtime environment and that `Moda.init()` runs before
150
+ the Anthropic client is created.
@@ -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.