@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,55 @@
1
+ # Golden reference: Next.js App Router + Vercel AI SDK + Moda
2
+
3
+ The complete finished integration for a minimal Next.js chat route. Match
4
+ this structure; adapt names to the target repository.
5
+
6
+ ## instrumentation.ts (project root)
7
+
8
+ Moda initializes once at server startup, before any AI SDK call.
9
+
10
+ ```typescript
11
+ export async function register() {
12
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
13
+ const { Moda } = await import('moda-ai');
14
+ await Moda.init(process.env.MODA_API_KEY!);
15
+ }
16
+ }
17
+ ```
18
+
19
+ ## app/api/chat/route.ts
20
+
21
+ Every AI SDK call passes `experimental_telemetry`, and conversation/user
22
+ context is scoped per request with `withContext` so concurrent requests
23
+ never leak into each other.
24
+
25
+ ```typescript
26
+ import { Moda, withContext } from 'moda-ai';
27
+ import { generateText } from 'ai';
28
+ import { openai } from '@ai-sdk/openai';
29
+
30
+ export async function POST(request: Request) {
31
+ const { sessionId, userId, message } = await request.json();
32
+
33
+ const result = await withContext(`session_${sessionId}`, userId, async () =>
34
+ generateText({
35
+ model: openai('gpt-4o'),
36
+ prompt: message,
37
+ experimental_telemetry: Moda.getVercelAITelemetry(),
38
+ }),
39
+ );
40
+
41
+ await Moda.flush();
42
+
43
+ return Response.json({ reply: result.text });
44
+ }
45
+ ```
46
+
47
+ ## What changed, and what did not
48
+
49
+ - `moda-ai` added to `package.json` dependencies.
50
+ - `instrumentation.ts` created (or extended) with the one-time `Moda.init`.
51
+ - Each AI SDK call site gained `experimental_telemetry` and scoped context.
52
+ - No `.env` file was created or edited; `MODA_API_KEY` is read from the
53
+ runtime environment.
54
+ - The raw `@ai-sdk/openai` provider client was NOT separately instrumented;
55
+ the AI SDK telemetry layer is the single integration surface.
@@ -0,0 +1,179 @@
1
+ ---
2
+ id: integration-node-vercel-ai-sdk
3
+ name: "Integrate Moda with the Vercel AI SDK (Node.js / TypeScript)"
4
+ version: 1.0.0
5
+ category: integration
6
+ targets:
7
+ language: node
8
+ frameworks:
9
+ - vercel-ai-sdk
10
+ providers: []
11
+ sdk_package: moda-ai
12
+ summary: "Instrument Vercel AI SDK calls (generateText, streamText, generateObject) with Moda telemetry in a Node.js or TypeScript app."
13
+ ---
14
+
15
+ # Integrate Moda with the Vercel AI 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/vercel-ai-sdk (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
+ ## Step 1: Confirm the Vercel AI SDK is the integration surface
36
+
37
+ Find the real LLM call sites: search for `generateText`, `streamText`,
38
+ `generateObject`, or `streamObject` imported from the `ai` package.
39
+
40
+ - If you find none, stop and report: this skill only applies to apps that
41
+ call the Vercel AI SDK directly.
42
+ - Instrument the Vercel AI SDK layer only. Do NOT also instrument raw
43
+ `openai` / `@anthropic-ai/sdk` clients that are used through the AI SDK;
44
+ that produces duplicate telemetry for the same call.
45
+
46
+ ## Step 2: Install the SDK
47
+
48
+ Install `moda-ai` with the package manager this repository already uses
49
+ (check the lockfile: `bun.lock`/`bun.lockb` means bun, `pnpm-lock.yaml` means
50
+ pnpm, `yarn.lock` means yarn, otherwise npm):
51
+
52
+ ```bash
53
+ npm install moda-ai
54
+ ```
55
+
56
+ If `moda-ai` is already in `package.json`, skip the install.
57
+
58
+ ## Step 3: Initialize Moda once at startup
59
+
60
+ `Moda.init` must run once, before the first AI SDK call. If a `Moda.init`
61
+ call already exists somewhere in the codebase, verify it runs at startup and
62
+ skip this step; never add a second call.
63
+
64
+ Next.js: put it in `instrumentation.ts` at the project root (create the file
65
+ if it does not exist):
66
+
67
+ ```typescript
68
+ export async function register() {
69
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
70
+ const { Moda } = await import('moda-ai');
71
+ await Moda.init(process.env.MODA_API_KEY!);
72
+ }
73
+ }
74
+ ```
75
+
76
+ Any other Node app: initialize at the top of the entry point, before the
77
+ first AI SDK call:
78
+
79
+ ```typescript
80
+ import { Moda } from 'moda-ai';
81
+
82
+ await Moda.init(process.env.MODA_API_KEY!);
83
+ ```
84
+
85
+ `Moda.init` is async. Await it so initialization completes before the first
86
+ LLM call; skipping the await risks the first call going uninstrumented.
87
+
88
+ ## Step 4: Pass Moda telemetry at every AI SDK call site
89
+
90
+ Add `experimental_telemetry: Moda.getVercelAITelemetry()` to every
91
+ `generateText` / `streamText` / `generateObject` / `streamObject` call:
92
+
93
+ ```typescript
94
+ import { Moda } from 'moda-ai';
95
+ import { generateText } from 'ai';
96
+ import { openai } from '@ai-sdk/openai';
97
+
98
+ const result = await generateText({
99
+ model: openai('gpt-4o'),
100
+ prompt: userMessage,
101
+ experimental_telemetry: Moda.getVercelAITelemetry(),
102
+ });
103
+ ```
104
+
105
+ Streaming works the same way; telemetry is captured after the stream
106
+ completes. If a call site already passes `experimental_telemetry`, stop and
107
+ report that call site instead of overwriting it.
108
+
109
+ ## Step 5: Set conversation and user context
110
+
111
+ Set a stable conversation id before each AI SDK call so multi-turn
112
+ conversations group together, and a user id when one user can be identified.
113
+ Set them before calling `Moda.getVercelAITelemetry()`; the telemetry config
114
+ captures them at call time.
115
+
116
+ For sequential code, use the global properties:
117
+
118
+ ```typescript
119
+ Moda.conversationId = `session_${sessionId}`;
120
+ Moda.userId = userId;
121
+ ```
122
+
123
+ For concurrent request handlers (any web server), use the scoped helper so
124
+ overlapping requests do not leak context into each other:
125
+
126
+ ```typescript
127
+ import { Moda, withContext } from 'moda-ai';
128
+
129
+ const result = await withContext(`session_${sessionId}`, userId, async () =>
130
+ generateText({
131
+ model: openai('gpt-4o'),
132
+ prompt: userMessage,
133
+ experimental_telemetry: Moda.getVercelAITelemetry(),
134
+ }),
135
+ );
136
+ ```
137
+
138
+ Reuse the session/thread identifier the app already has. If no session
139
+ identifier exists anywhere, stop and report; do not invent one.
140
+
141
+ ## Step 6: Attribute managed prompts (only if the repo uses them)
142
+
143
+ Only if this repository has a `.moda/prompts.yml` manifest: render managed
144
+ prompts through the SDK before creating the telemetry config, so traces point
145
+ at exact prompt versions. See
146
+ [references/prompt-attribution.md](references/prompt-attribution.md).
147
+ If there is no prompt manifest, skip this step.
148
+
149
+ ## Step 7: Flush before exit
150
+
151
+ For short-lived scripts and CLIs, flush before the process exits:
152
+
153
+ ```typescript
154
+ await Moda.flush();
155
+ ```
156
+
157
+ For long-running servers, flush on shutdown:
158
+
159
+ ```typescript
160
+ process.on('SIGTERM', async () => {
161
+ await Moda.flush();
162
+ await Moda.shutdown();
163
+ process.exit(0);
164
+ });
165
+ ```
166
+
167
+ ## Step 8: Validate
168
+
169
+ Report the packages installed, files edited, and any step a human still needs
170
+ to do (for example setting `MODA_API_KEY` in the deploy environment). Then
171
+ verify telemetry reaches Moda:
172
+
173
+ ```bash
174
+ moda doctor --online --json
175
+ ```
176
+
177
+ The report should show recent traces. If it does not, confirm `MODA_API_KEY`
178
+ is available in the runtime environment and that `Moda.init()` runs before
179
+ the first AI SDK call.
@@ -0,0 +1,31 @@
1
+ # Prompt attribution with the Vercel AI SDK
2
+
3
+ Only relevant when the repository manages prompts with Moda
4
+ (`.moda/prompts.yml` exists and prompts are synced with `moda prompts sync`).
5
+
6
+ Render the managed prompt through the SDK, then create the telemetry config.
7
+ `Moda.getVercelAITelemetry()` includes the rendered prompt's metadata
8
+ (`moda.prompt_key`, `moda.prompt_id`, `moda.prompt_version`,
9
+ `moda.prompt_version_id`) in `experimental_telemetry.metadata`, so production
10
+ traces point at the exact prompt version:
11
+
12
+ ```typescript
13
+ import { Moda } from 'moda-ai';
14
+ import { generateText } from 'ai';
15
+ import { openai } from '@ai-sdk/openai';
16
+
17
+ const rendered = Moda.prompt('support.triage').render({
18
+ ticket: { text: userMessage },
19
+ });
20
+
21
+ const result = await generateText({
22
+ model: openai('gpt-4o'),
23
+ messages: rendered.messages,
24
+ experimental_telemetry: Moda.getVercelAITelemetry(),
25
+ });
26
+ ```
27
+
28
+ Order matters: render the prompt before calling
29
+ `Moda.getVercelAITelemetry()` so prompt metadata is attached.
30
+
31
+ Full reference: https://docs.moda.dev/ingestion/vercel-ai-sdk
@@ -0,0 +1,66 @@
1
+ # Golden reference: FastAPI + Anthropic SDK + Moda
2
+
3
+ The complete finished integration for a minimal FastAPI chat service that
4
+ calls the Anthropic SDK directly. Match this structure; adapt names to the
5
+ target repository.
6
+
7
+ ## app/main.py
8
+
9
+ ```python
10
+ import os
11
+ from contextlib import asynccontextmanager
12
+
13
+ import moda
14
+ from anthropic import Anthropic
15
+ from fastapi import FastAPI
16
+ from moda import set_conversation_id, set_user_id
17
+ from pydantic import BaseModel
18
+
19
+ # Initialize Moda before creating any LLM client so instrumentation is
20
+ # active. After this, every messages.create call is captured automatically.
21
+ moda.init(os.environ["MODA_API_KEY"])
22
+
23
+ client = Anthropic()
24
+
25
+
26
+ @asynccontextmanager
27
+ async def lifespan(app: FastAPI):
28
+ yield
29
+ # Flush pending telemetry on shutdown.
30
+ moda.flush()
31
+
32
+
33
+ app = FastAPI(lifespan=lifespan)
34
+
35
+
36
+ class ChatRequest(BaseModel):
37
+ session_id: str
38
+ user_id: str
39
+ message: str
40
+
41
+
42
+ @app.post("/chat")
43
+ def chat(request: ChatRequest):
44
+ # Scoped context: safe under concurrent requests.
45
+ with set_conversation_id(f"session_{request.session_id}"):
46
+ with set_user_id(request.user_id):
47
+ response = client.messages.create(
48
+ model="claude-sonnet-4-20250514",
49
+ max_tokens=1024,
50
+ messages=[{"role": "user", "content": request.message}],
51
+ )
52
+ reply = "".join(block.text for block in response.content if block.type == "text")
53
+ return {"reply": reply}
54
+ ```
55
+
56
+ ## What changed, and what did not
57
+
58
+ - `moda-ai` added to the project dependencies.
59
+ - One `moda.init` call added at module import time, before `Anthropic()` is
60
+ constructed.
61
+ - The handler wraps its existing Anthropic call in the scoped context
62
+ managers; the `messages.create` call itself is unchanged — instrumentation
63
+ is automatic.
64
+ - Shutdown flushes telemetry via the FastAPI lifespan hook.
65
+ - No `.env` file was created or edited; `MODA_API_KEY` is read from the
66
+ runtime environment.
@@ -0,0 +1,153 @@
1
+ ---
2
+ id: integration-python-anthropic
3
+ name: "Integrate Moda with the Anthropic SDK (Python)"
4
+ version: 1.0.0
5
+ category: integration
6
+ targets:
7
+ language: python
8
+ frameworks: []
9
+ providers:
10
+ - anthropic
11
+ sdk_package: moda-ai
12
+ summary: "Auto-instrument anthropic client calls with Moda in a Python app; no per-call changes required."
13
+ ---
14
+
15
+ # Integrate Moda with the Anthropic SDK (Python)
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.
22
+
23
+ ## Safety rules (apply to every step)
24
+
25
+ - Only touch the repository you were launched in.
26
+ - Never write the `MODA_API_KEY` value (or any secret) into source files, and
27
+ never create or edit `.env` files. The key belongs in the runtime
28
+ environment or a secret manager.
29
+ - Make the smallest correct change. Match the existing code style and imports.
30
+ - Do not commit unless the user asks.
31
+ - If a step is ambiguous in this repository, stop and report rather than
32
+ guessing.
33
+
34
+ ## Step 1: Confirm the raw Anthropic client is the integration surface
35
+
36
+ Find the real LLM call sites: `client.messages.create(...)` or
37
+ `client.messages.stream(...)` on a client constructed with `Anthropic()`
38
+ from the `anthropic` package.
39
+
40
+ - If the Anthropic calls go through a wrapper framework instead (LangChain,
41
+ CrewAI, the Claude Agent SDK, or similar), stop: instrument the wrapper
42
+ layer with its dedicated skill, never the underlying provider client.
43
+ - If the repo wraps its Anthropic client in a helper module (for example
44
+ `app/llm.py` exporting a shared client), plan to instrument at that one
45
+ place; do not edit every call site.
46
+ - If there are no Anthropic call sites at all, stop and report.
47
+
48
+ ## Step 2: Install the SDK
49
+
50
+ Install `moda-ai` with the package manager this repository already uses
51
+ (`uv.lock` means uv, `poetry.lock` means poetry, otherwise pip):
52
+
53
+ ```bash
54
+ pip install moda-ai
55
+ ```
56
+
57
+ With uv:
58
+
59
+ ```bash
60
+ uv add moda-ai
61
+ ```
62
+
63
+ With poetry:
64
+
65
+ ```bash
66
+ poetry add moda-ai
67
+ ```
68
+
69
+ If `moda-ai` is already a dependency, skip the install. `moda-ai` includes
70
+ the OpenAI and Anthropic instrumentation packages; nothing else is needed
71
+ for this integration.
72
+
73
+ ## Step 3: Initialize Moda once at startup, before the client is created
74
+
75
+ Instrumentation is automatic: once `moda.init` has run, every
76
+ `messages.create` / `messages.stream` call (including extended thinking and
77
+ tool use) is captured with no per-call changes.
78
+
79
+ `moda.init` is synchronous and must run before the Anthropic client is
80
+ constructed. If a `moda.init` call already exists, verify it runs at startup
81
+ before client creation and skip this step; never add a second call.
82
+
83
+ ```python
84
+ import os
85
+
86
+ import moda
87
+ from anthropic import Anthropic
88
+
89
+ moda.init(os.environ["MODA_API_KEY"])
90
+
91
+ client = Anthropic()
92
+ ```
93
+
94
+ For web frameworks, initialize at import time of the application module (as
95
+ above) so the client-creation ordering holds; requirement: `moda.init` runs
96
+ before any `Anthropic()` construction.
97
+
98
+ ## Step 4: Set conversation and user context
99
+
100
+ Set a stable conversation id before each LLM call so multi-turn
101
+ conversations group together, and a user id when one user can be identified.
102
+
103
+ For sequential code, use the module-level properties:
104
+
105
+ ```python
106
+ moda.conversation_id = f"session_{session_id}"
107
+ moda.user_id = user_id
108
+ ```
109
+
110
+ For concurrent request handlers (any web server), use the scoped context
111
+ managers so overlapping requests do not leak context into each other:
112
+
113
+ ```python
114
+ from moda import set_conversation_id, set_user_id
115
+
116
+ with set_conversation_id(f"session_{session_id}"):
117
+ with set_user_id(user_id):
118
+ response = client.messages.create(
119
+ model="claude-sonnet-4-20250514",
120
+ max_tokens=1024,
121
+ messages=[{"role": "user", "content": user_message}],
122
+ )
123
+ ```
124
+
125
+ Reuse the session/thread identifier the app already has. If no session
126
+ identifier exists anywhere, the SDK falls back to hashing the first user
127
+ message plus the system prompt — acceptable only for simple chatbots that
128
+ pass full message history on every call. If this repo is an agent framework
129
+ or does not pass full history, an explicit conversation id is required; stop
130
+ and report if you cannot find a session identifier to use.
131
+
132
+ ## Step 5: Flush before exit
133
+
134
+ Flush pending telemetry before the process exits (short-lived scripts) or on
135
+ shutdown (servers):
136
+
137
+ ```python
138
+ moda.flush()
139
+ ```
140
+
141
+ ## Step 6: Validate
142
+
143
+ Report the packages installed, files edited, and any step a human still needs
144
+ to do (for example setting `MODA_API_KEY` in the deploy environment). Then
145
+ verify telemetry reaches Moda:
146
+
147
+ ```bash
148
+ moda doctor --online --json
149
+ ```
150
+
151
+ The report should show recent traces. If it does not, confirm `MODA_API_KEY`
152
+ is available in the runtime environment and that `moda.init()` runs before
153
+ the Anthropic client is created.
@@ -0,0 +1,61 @@
1
+ # Golden reference: Claude Agent SDK + Moda
2
+
3
+ The complete finished integration for a minimal Claude Agent SDK script.
4
+ Match this structure; adapt names to the target repository.
5
+
6
+ ## agent.py
7
+
8
+ ```python
9
+ import asyncio
10
+ import os
11
+
12
+ import moda
13
+ from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient
14
+
15
+ # Initialize Moda before creating the ClaudeSDKClient. When
16
+ # moda-claude-agent-sdk is installed, the instrumentor is enabled
17
+ # automatically.
18
+ moda.init(os.environ["MODA_API_KEY"])
19
+
20
+ # Explicit conversation and user context so all turns group together.
21
+ moda.conversation_id = "session_123"
22
+ moda.user_id = "user_456"
23
+
24
+
25
+ async def main():
26
+ options = ClaudeAgentOptions(model="claude-sonnet-4-20250514")
27
+
28
+ async with ClaudeSDKClient(options=options) as client:
29
+ await client.query("What is the capital of France?")
30
+
31
+ # Consume the full stream: token usage arrives in the final
32
+ # ResultMessage.
33
+ async for msg in client.receive_response():
34
+ print(msg)
35
+
36
+
37
+ asyncio.run(main())
38
+
39
+ # Flush pending telemetry before exit.
40
+ moda.flush()
41
+ ```
42
+
43
+ ## Dependencies
44
+
45
+ Both packages are required; `moda-claude-agent-sdk` is not included with
46
+ `moda-ai`:
47
+
48
+ ```bash
49
+ pip install moda-ai moda-claude-agent-sdk claude-agent-sdk
50
+ ```
51
+
52
+ ## What changed, and what did not
53
+
54
+ - `moda-ai` and `moda-claude-agent-sdk` added to the project dependencies.
55
+ - One `moda.init` call added at startup, before `ClaudeSDKClient` is
56
+ constructed.
57
+ - Conversation and user ids set before the agent runs.
58
+ - The agent code itself is unchanged; the instrumentor wraps
59
+ `ClaudeSDKClient` automatically.
60
+ - No `.env` file was created or edited; `MODA_API_KEY` is read from the
61
+ runtime environment.
@@ -0,0 +1,129 @@
1
+ ---
2
+ id: integration-python-claude-agent-sdk
3
+ name: "Integrate Moda with the Claude Agent SDK (Python)"
4
+ version: 1.0.0
5
+ category: integration
6
+ targets:
7
+ language: python
8
+ frameworks:
9
+ - claude-agent-sdk
10
+ providers: []
11
+ sdk_package: moda-claude-agent-sdk
12
+ summary: "Instrument Claude Agent SDK sessions (ClaudeSDKClient) with Moda via the dedicated moda-claude-agent-sdk instrumentor."
13
+ ---
14
+
15
+ # Integrate Moda with the Claude Agent SDK (Python)
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/claude-agent-sdk.
21
+
22
+ ## Safety rules (apply to every step)
23
+
24
+ - Only touch the repository you were launched in.
25
+ - Never write the `MODA_API_KEY` value (or any secret) into source files, and
26
+ never create or edit `.env` files. The key belongs in the runtime
27
+ environment or a secret manager.
28
+ - Make the smallest correct change. Match the existing code style and imports.
29
+ - Do not commit unless the user asks.
30
+ - If a step is ambiguous in this repository, stop and report rather than
31
+ guessing.
32
+
33
+ ## Why this integration needs a separate package
34
+
35
+ The Claude Agent SDK spawns Claude as a subprocess rather than making direct
36
+ Anthropic API calls, so standard Anthropic API instrumentation does not
37
+ fire. The `moda-claude-agent-sdk` package provides a dedicated instrumentor
38
+ that wraps `ClaudeSDKClient` to capture agent-level spans (token usage, tool
39
+ calls, turns, session metadata). The Claude Agent SDK is Python-only; if
40
+ this repository is Node.js, stop and report — there is no Node.js support.
41
+
42
+ ## Step 1: Find the Claude Agent SDK call sites
43
+
44
+ Find where the application constructs `ClaudeSDKClient` (from
45
+ `claude_agent_sdk`). If there are none, stop and report: this skill only
46
+ applies to apps using the Claude Agent SDK.
47
+
48
+ ## Step 2: Install the SDK packages
49
+
50
+ Install both `moda-ai` and `moda-claude-agent-sdk` with the package manager
51
+ this repository already uses (`uv.lock` means uv, `poetry.lock` means
52
+ poetry, otherwise pip). `moda-claude-agent-sdk` is NOT included with
53
+ `moda-ai`; it must be installed separately:
54
+
55
+ ```bash
56
+ pip install moda-ai moda-claude-agent-sdk
57
+ ```
58
+
59
+ With uv:
60
+
61
+ ```bash
62
+ uv add moda-ai moda-claude-agent-sdk
63
+ ```
64
+
65
+ With poetry:
66
+
67
+ ```bash
68
+ poetry add moda-ai moda-claude-agent-sdk
69
+ ```
70
+
71
+ If both are already dependencies, skip the install.
72
+
73
+ ## Step 3: Initialize Moda once at startup, before the client is created
74
+
75
+ `moda.init` is synchronous, automatically enables the Claude Agent SDK
76
+ instrumentor when `moda-claude-agent-sdk` is installed, and must run before
77
+ any `ClaudeSDKClient` is constructed. If a `moda.init` call already exists,
78
+ verify it runs at startup and skip this step; never add a second call.
79
+
80
+ ```python
81
+ import os
82
+
83
+ import moda
84
+
85
+ moda.init(os.environ["MODA_API_KEY"])
86
+ ```
87
+
88
+ ## Step 4: Set conversation and user context
89
+
90
+ Set a stable conversation id (and user id when one user can be identified)
91
+ before running the agent, so all turns of a session group together:
92
+
93
+ ```python
94
+ moda.conversation_id = f"session_{session_id}"
95
+ moda.user_id = user_id
96
+ ```
97
+
98
+ Reuse the session identifier the app already has. If no session identifier
99
+ exists anywhere, stop and report; do not invent one.
100
+
101
+ ## Step 5: Consume the full response stream
102
+
103
+ Token usage comes from the `ResultMessage` at the end of the stream. Verify
104
+ the code consumes the full async generator from `receive_response()`; if it
105
+ breaks out early, token counts will be missing. Do not restructure working
106
+ code — just confirm the stream is fully consumed, and report if it is not.
107
+
108
+ ## Step 6: Flush before exit
109
+
110
+ Flush pending telemetry before the process exits:
111
+
112
+ ```python
113
+ moda.flush()
114
+ ```
115
+
116
+ ## Step 7: Validate
117
+
118
+ Report the packages installed, files edited, and any step a human still needs
119
+ to do (for example setting `MODA_API_KEY` in the deploy environment). Then
120
+ verify telemetry reaches Moda:
121
+
122
+ ```bash
123
+ moda doctor --online --json
124
+ ```
125
+
126
+ The report should show recent traces. If it does not: confirm
127
+ `moda-claude-agent-sdk` is installed (it is separate from `moda-ai`), that
128
+ `moda.init()` runs before `ClaudeSDKClient` is created, and that
129
+ `MODA_API_KEY` is available in the runtime environment.