watchmyagents 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MinedorFBM and Watch My Agents contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # Watch My Agents
2
+
3
+ **Security observability for AI agents.** A zero-dependency CLI + SDK that captures every action your AI agents take — tool calls, prompts, state transitions, errors, multi-agent comms — into local NDJSON logs. Built for security audits, not just token counting.
4
+
5
+ Designed around three guarantees:
6
+
7
+ 1. **Local-first.** Raw payloads (prompts, outputs, tool arguments) stay 100% on your machine. Nothing leaves unless you explicitly opt in.
8
+ 2. **Trace everything, not just what costs tokens.** A `web_fetch` to a suspicious URL carries zero tokens but is exactly what a security audit needs to see.
9
+ 3. **Zero dependencies.** Only Node.js 18+ built-ins. No telemetry, no phone-home, no hidden network calls.
10
+
11
+ ---
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install -g watchmyagents
17
+ ```
18
+
19
+ ## Quickstart — monitor an Anthropic Managed Agent
20
+
21
+ You'll need:
22
+ - An Anthropic API key (`sk-ant-…`)
23
+ - The `agent_id` of the agent you want to monitor (from [console.anthropic.com](https://console.anthropic.com))
24
+
25
+ ```bash
26
+ export ANTHROPIC_API_KEY="sk-ant-..."
27
+
28
+ wma-fetch --agent-id agent_01XaN... --since 1h
29
+ wma-inspect
30
+ ```
31
+
32
+ That's it. You'll see a security-focused summary of everything the agent did:
33
+
34
+ ```
35
+ ━━━ WatchMyAgents log inspector ━━━
36
+ entries : 90
37
+ sessions : 2 (session_end entries: 2)
38
+ model : claude-sonnet-4-6
39
+ window : 2026-05-23T05:32:08Z → 2026-05-23T06:12:40Z
40
+ status : ok=90 error=0
41
+
42
+ ── Tokens ──
43
+ total : 811,798 (in=26 out=22,996 cache_r=492,220 cache_w=296,556)
44
+
45
+ ── By tool ──
46
+ web_search calls= 20 tokens= 0
47
+ web_fetch calls= 2 tokens= 0
48
+
49
+ ── By action_type ──
50
+ llm_call calls= 12 tokens= 811798
51
+ state_transition calls= 28
52
+ user_message calls= 7
53
+ thinking calls= 9
54
+ message calls= 10
55
+ tool_use calls= 22
56
+
57
+ ── Top destinations (tool inputs) ──
58
+ 1× web_search "AI agent security attack vectors prompt injection..."
59
+ 1× web_fetch https://genai.owasp.org/2025/12/09/owasp-genai-...
60
+
61
+ ── Action sequences (top transitions) ──
62
+ 19× 22.1% state_transition → state_transition
63
+ 17× 19.8% tool_use → tool_use
64
+ ...
65
+
66
+ ── Tool latency ──
67
+ web_search n= 20 p50=3,744 ms p95=4,009 ms max=4,009 ms
68
+ web_fetch n= 2 p50=1,477 ms p95=1,477 ms
69
+
70
+ ── Rate metrics ──
71
+ tokens/min : 721
72
+ calls/min : 0.08
73
+ ```
74
+
75
+ ## What gets logged
76
+
77
+ Each line of the NDJSON file is one agent action. The 18 `action_type` values captured today:
78
+
79
+ | `action_type` | When emitted |
80
+ |---|---|
81
+ | `user_message` | A prompt is sent to the agent |
82
+ | `user_interrupt` | Manual mid-execution stop |
83
+ | `tool_confirmation` | Approve / deny a tool call gated by a permission policy |
84
+ | `custom_tool_result` | Orchestrator returns a custom tool result |
85
+ | `message` | Agent text response |
86
+ | `thinking` | Agent reasoning block |
87
+ | `llm_call` | Model inference call (with token usage) |
88
+ | `tool_use` | Pre-built agent tool invoked (web_search, web_fetch, bash, …) |
89
+ | `mcp_tool_use` | MCP server tool invoked |
90
+ | `custom_tool_use` | Custom tool defined by the orchestrator |
91
+ | `context_compacted` | Context window saturated — history compacted |
92
+ | `thread_created` | A multi-agent thread was created |
93
+ | `thread_message_sent` / `_received` | Inter-agent communication in multi-agent sessions |
94
+ | `config_change` | Session config (system prompt, tools, …) was updated mid-flight ⚠️ |
95
+ | `state_transition` | Session/thread `running`/`idle`/`rescheduled`/`terminated` |
96
+ | `session_error` | Error during session processing |
97
+ | `session_end` | Synthetic marker at end of each fetch (tokens summary) |
98
+
99
+ Each entry carries: `id`, `agent_id`, `framework`, `timestamp`, `action_type`, `tool_name`, `model`, `duration_ms`, `tokens_used`, `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_creation_tokens`, `status`, `error`, `sequence_number`, `session_id`, `input`, `output`.
100
+
101
+ **The `input` and `output` fields contain the raw payload** (tool arguments, agent responses, queries). They never leave your machine.
102
+
103
+ ## CLI reference
104
+
105
+ ### `wma-fetch` — pull events from Anthropic Managed Agents
106
+
107
+ ```bash
108
+ wma-fetch --agent-id <agent_id> [--session-id <sess_id>] [--since 1h]
109
+ [--log-dir ./watchmyagents-logs] [--dump-raw]
110
+ ```
111
+
112
+ | Flag | Effect |
113
+ |---|---|
114
+ | `--agent-id agent_xxx` | Required — Anthropic agent identifier |
115
+ | `--since 1h` / `24h` / `7d` | Fetch window (default: all) |
116
+ | `--session-id sesn_xxx` | Limit to a single session |
117
+ | `--log-dir ./logs` | Where to write NDJSON (default `./watchmyagents-logs`) |
118
+ | `--dump-raw` | Also save raw API events alongside (forensic / debugging) |
119
+ | `--api-key sk-ant-…` | Override the `ANTHROPIC_API_KEY` env var |
120
+
121
+ Logs land in `./watchmyagents-logs/<agent_id>/<date>.ndjson` (file mode `0600`, dir `0700`).
122
+
123
+ ### `wma-inspect` — audit the logs
124
+
125
+ ```bash
126
+ wma-inspect [path]
127
+ ```
128
+
129
+ `path` can be a single `.ndjson` file or a directory (default: `./watchmyagents-logs`).
130
+
131
+ Outputs sections aligned with security audit needs: tokens summary, by-tool / by-action-type breakdowns, top tool destinations (URLs / queries), action-sequence transitions, tool error rates, p50/p95/max latency per tool, rate metrics.
132
+
133
+ ## Automating (cron)
134
+
135
+ For continuous monitoring, run `wma-fetch` on a cron:
136
+
137
+ ```cron
138
+ # Every 15 minutes
139
+ */15 * * * * cd /path/to/project && wma-fetch --agent-id agent_01XaN... --since 20m
140
+ ```
141
+
142
+ Or for daily reports:
143
+
144
+ ```cron
145
+ # Once per night, fetch the full last 24h
146
+ 5 0 * * * cd /path/to/project && wma-fetch --agent-id agent_01XaN... --since 25h
147
+ ```
148
+
149
+ ## Data sovereignty model
150
+
151
+ WatchMyAgents is built so that **your prompts and outputs never have to leave your machine**:
152
+
153
+ | Where | What lives there |
154
+ |---|---|
155
+ | **Your machine** (`./watchmyagents-logs/`) | Full NDJSON with all prompts, tool inputs, agent outputs. `chmod 600` on every file. |
156
+ | **Anthropic API** | Where the agent runs. WMA pulls events via the public REST API only. |
157
+ | **WMA infrastructure** | **Nothing today.** Future opt-in telemetry will ship only anonymized metadata (counts, timings, hashes) — never raw payloads. |
158
+
159
+ This is the "local-first" guarantee. It is the product, not a marketing claim.
160
+
161
+ ## Security
162
+
163
+ WMA requires your Anthropic API key to call the Managed Agents REST API on your behalf. The key:
164
+
165
+ - Is read from the `ANTHROPIC_API_KEY` env var or the `--api-key` flag
166
+ - Is **never** written to disk, **never** logged, **never** transmitted anywhere except `api.anthropic.com` over HTTPS
167
+ - Is only ever held in process memory for the duration of a `wma-fetch` run
168
+
169
+ For added safety, generate a **workspace-scoped** API key with read-only permissions on the agents you want to monitor: [console.anthropic.com → API Keys](https://console.anthropic.com/settings/keys).
170
+
171
+ Report vulnerabilities via [SECURITY.md](./SECURITY.md).
172
+
173
+ ## Status
174
+
175
+ - ✅ Anthropic Managed Agents (post-hoc fetch + audit)
176
+ - 🚧 Encrypted upload to customer's own cloud (S3/GCS/Azure with `age` public-key encryption)
177
+ - 🚧 Anonymized telemetry to WMA cloud (opt-in, freemium model)
178
+ - 🚧 Shield product — real-time policy gating via `user.tool_confirmation` + `user.interrupt`
179
+ - 🚧 Adapters for in-process agents (Claude SDK, OpenAI, LangChain, generic) — code present in `src/adapters/` but unverified against the new Modèle C architecture; documentation will follow once re-validated
180
+
181
+ ## License
182
+
183
+ [MIT](./LICENSE)
package/SECURITY.md ADDED
@@ -0,0 +1,78 @@
1
+ # Security Policy
2
+
3
+ ## How Watch My Agents handles your secrets
4
+
5
+ WMA is designed so that **your data and credentials stay on your machine**. This document describes how, and where the trust boundaries lie.
6
+
7
+ ### Your Anthropic API key
8
+
9
+ WMA needs your Anthropic API key to call the Managed Agents REST API on your behalf.
10
+
11
+ | Property | Behavior |
12
+ |---|---|
13
+ | **Source** | Environment variable `ANTHROPIC_API_KEY` or `--api-key` CLI flag |
14
+ | **Storage** | Held in process memory for the duration of a `wma-fetch` run. Never persisted to disk by WMA. |
15
+ | **Network** | Sent only to `api.anthropic.com` over HTTPS with strict certificate verification (`rejectUnauthorized: true`) |
16
+ | **Logging** | The key is never written to NDJSON logs, never printed in error messages, never included in any export |
17
+ | **Telemetry** | WMA performs zero telemetry today. No phone-home, no usage reporting. |
18
+
19
+ **Recommendation:** generate a workspace-scoped API key with read-only permissions on the agents you want to monitor. See [Anthropic Console → API Keys](https://console.anthropic.com/settings/keys).
20
+
21
+ ### Local log files
22
+
23
+ `wma-fetch` writes NDJSON files to `./watchmyagents-logs/<agent_id>/<date>.ndjson` with the following protections:
24
+
25
+ - Directory mode: `0700` (only your user can read/list)
26
+ - File mode: `0600` (only your user can read/write)
27
+ - No encryption at rest by default — files are plaintext JSON Lines on disk
28
+
29
+ **Add `watchmyagents-logs/` to your `.gitignore`** to avoid committing prompts and tool outputs to a repo.
30
+
31
+ ### What WMA does NOT do
32
+
33
+ - ❌ Does not phone home, telemetry, analytics, or usage reporting
34
+ - ❌ Does not send any data to WMA-controlled servers
35
+ - ❌ Does not store, log, or transmit your Anthropic API key anywhere except `api.anthropic.com`
36
+ - ❌ Does not require an account, signup, or license key
37
+
38
+ ## Threat model
39
+
40
+ WMA is built to give visibility into your AI agent's behavior. It is **observational**, not preventive.
41
+
42
+ ### What WMA defends against
43
+
44
+ - **Blind spots in agent behavior.** Tool calls, prompts, state transitions, errors are all captured for after-the-fact analysis.
45
+ - **Token-only observability tools.** WMA captures every action including zero-token ones (`tool_use`, `state_transition`, etc.) that are the most security-relevant.
46
+ - **Vendor lock-in.** NDJSON is portable; you own the data.
47
+
48
+ ### What WMA does NOT defend against
49
+
50
+ - **Real-time attack prevention.** WMA observes after events occur. For inline policy gating, see the upcoming Shield product.
51
+ - **A compromised host.** If an attacker has read access to your user account, they can read the log files. Consider encryption at rest (filesystem-level, or future opt-in via `age`) for sensitive environments.
52
+ - **Tampering with local logs.** Files are append-only by convention, not enforced. A future release will add a per-line hash chain for tamper-evident audit.
53
+ - **A compromised Anthropic API.** WMA trusts the events delivered by Anthropic. This is out of scope.
54
+
55
+ ## Supply chain
56
+
57
+ - All code is open source on [GitHub](https://github.com/minedorfbm/watchmyagents)
58
+ - Zero runtime dependencies (uses Node.js 18+ built-ins only)
59
+ - One dev dependency (`@anthropic-ai/sdk`) for the optional adapter examples
60
+ - Future releases will use `npm publish --provenance` for SLSA build attestation
61
+
62
+ ## Reporting a vulnerability
63
+
64
+ If you discover a security issue, **please do NOT open a public GitHub issue.**
65
+
66
+ Email: [minedor@watchmyagents.com](mailto:minedor@watchmyagents.com)
67
+
68
+ Include:
69
+ - A description of the issue and its impact
70
+ - Steps to reproduce
71
+ - The version of WMA affected (`npm list -g watchmyagents`)
72
+ - Your suggested fix, if any
73
+
74
+ We aim to acknowledge reports within 72 hours and provide an initial assessment within 7 days. Coordinated disclosure preferred.
75
+
76
+ ## Updates
77
+
78
+ This policy may be updated as the product evolves (notably when Shield, encrypted exports, and anonymized telemetry ship). Watch the repository for changes.
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "watchmyagents",
3
+ "version": "0.1.0",
4
+ "description": "Security observability for AI agents — local-first NDJSON capture of every agent action (tool calls, prompts, state transitions, errors). Built for security audits, not just token counting.",
5
+ "type": "module",
6
+ "main": "./src/index.cjs",
7
+ "module": "./src/index.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./src/index.js",
11
+ "require": "./src/index.cjs"
12
+ },
13
+ "./adapters/claude": "./src/adapters/claude.js",
14
+ "./adapters/openai": "./src/adapters/openai.js",
15
+ "./adapters/langchain": "./src/adapters/langchain.js",
16
+ "./adapters/generic": "./src/adapters/generic.js"
17
+ },
18
+ "files": [
19
+ "src/",
20
+ "scripts/inspect.js",
21
+ "scripts/fetch-anthropic.js",
22
+ "README.md",
23
+ "SECURITY.md",
24
+ "LICENSE"
25
+ ],
26
+ "bin": {
27
+ "wma-inspect": "scripts/inspect.js",
28
+ "wma-fetch": "scripts/fetch-anthropic.js"
29
+ },
30
+ "scripts": {
31
+ "inspect": "node scripts/inspect.js",
32
+ "fetch": "node scripts/fetch-anthropic.js",
33
+ "example": "node examples/claude-agent/index.js"
34
+ },
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "dependencies": {},
39
+ "devDependencies": {
40
+ "@anthropic-ai/sdk": "latest"
41
+ },
42
+ "keywords": [
43
+ "ai",
44
+ "agents",
45
+ "monitoring",
46
+ "logging",
47
+ "security",
48
+ "observability",
49
+ "cybersecurity",
50
+ "anthropic",
51
+ "claude",
52
+ "managed-agents",
53
+ "audit",
54
+ "ndjson"
55
+ ],
56
+ "author": "MinedorFBM <minedor@watchmyagents.com>",
57
+ "license": "MIT",
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "git+https://github.com/minedorfbm/watchmyagents.git"
61
+ },
62
+ "homepage": "https://github.com/minedorfbm/watchmyagents#readme",
63
+ "bugs": {
64
+ "url": "https://github.com/minedorfbm/watchmyagents/issues"
65
+ }
66
+ }
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ // wma-fetch — pull session events from Anthropic Managed Agents and
3
+ // write them as WatchMyAgents NDJSON, ready for `wma-inspect`.
4
+ //
5
+ // Usage:
6
+ // wma-fetch --agent-id agent_xxx [--session-id sess_xxx] [--since 1h]
7
+ // [--log-dir ./watchmyagents-logs] [--dump-raw]
8
+ //
9
+ // API key is read from --api-key or env ANTHROPIC_API_KEY.
10
+
11
+ import { mkdir, appendFile } from 'node:fs/promises';
12
+ import { join, resolve } from 'node:path';
13
+ import { Logger } from '../src/logger.js';
14
+ import { TokenTracker } from '../src/tokens.js';
15
+ import {
16
+ getAgent, listSessions, fetchSessionEntries, fetchRawEvents,
17
+ } from '../src/sources/anthropic-managed.js';
18
+
19
+ function parseArgs(argv) {
20
+ const out = {};
21
+ for (let i = 0; i < argv.length; i++) {
22
+ const a = argv[i];
23
+ if (a.startsWith('--')) {
24
+ const k = a.slice(2);
25
+ const n = argv[i + 1];
26
+ if (n == null || n.startsWith('--')) out[k] = true;
27
+ else { out[k] = n; i++; }
28
+ }
29
+ }
30
+ return out;
31
+ }
32
+
33
+ function parseSince(s) {
34
+ if (!s || s === true) return null;
35
+ const m = String(s).match(/^(\d+)\s*([smhd])$/);
36
+ if (m) {
37
+ const n = parseInt(m[1], 10);
38
+ const mult = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 }[m[2]];
39
+ return new Date(Date.now() - n * mult);
40
+ }
41
+ const d = new Date(s);
42
+ if (isNaN(d)) throw new Error(`invalid --since value: ${s}`);
43
+ return d;
44
+ }
45
+
46
+ function die(msg, code = 1) { process.stderr.write(`${msg}\n`); process.exit(code); }
47
+
48
+ async function main() {
49
+ const args = parseArgs(process.argv.slice(2));
50
+ const apiKey = args['api-key'] || process.env.ANTHROPIC_API_KEY;
51
+ const agentId = args['agent-id'];
52
+ const sessionId = args['session-id'];
53
+ const since = args.since ? parseSince(args.since) : null;
54
+ const logDir = resolve(args['log-dir'] || './watchmyagents-logs');
55
+ const dumpRaw = !!args['dump-raw'];
56
+
57
+ if (!apiKey) die('error: --api-key or ANTHROPIC_API_KEY required');
58
+ if (!agentId) die('error: --agent-id required (e.g. agent_01XaNB4M88ZvcW8FoQ5GC14A)');
59
+
60
+ process.stdout.write(`[wma-fetch] resolving agent ${agentId}…\n`);
61
+ const agent = await getAgent(apiKey, agentId).catch(e => die(`failed to GET agent: ${e.message}`));
62
+ const rawModel = agent.model || agent.config?.model || null;
63
+ // API may return model as { id, speed } object or as a plain string.
64
+ const model = (rawModel && typeof rawModel === 'object') ? (rawModel.id || null) : rawModel;
65
+ process.stdout.write(`[wma-fetch] model: ${model || '(unknown)'}\n`);
66
+
67
+ let sessions;
68
+ if (sessionId) {
69
+ sessions = [{ id: sessionId, created_at: new Date().toISOString() }];
70
+ } else {
71
+ process.stdout.write(`[wma-fetch] listing sessions${since ? ` since ${since.toISOString()}` : ''}…\n`);
72
+ sessions = await listSessions(apiKey, { agentId, since })
73
+ .catch(e => die(`failed to list sessions: ${e.message}`));
74
+ }
75
+
76
+ if (sessions.length === 0) {
77
+ process.stdout.write('[wma-fetch] no sessions to fetch\n');
78
+ return;
79
+ }
80
+ process.stdout.write(`[wma-fetch] ${sessions.length} session(s) to fetch\n`);
81
+
82
+ let totalEntries = 0;
83
+ for (const s of sessions) {
84
+ const sid = s.id;
85
+ process.stdout.write(`\n[wma-fetch] session ${sid}\n`);
86
+
87
+ if (dumpRaw) {
88
+ const rawPath = join(logDir, agentId, `raw-${sid}.jsonl`);
89
+ await mkdir(join(logDir, agentId), { recursive: true, mode: 0o700 });
90
+ for await (const ev of fetchRawEvents(apiKey, sid)) {
91
+ await appendFile(rawPath, JSON.stringify(ev) + '\n', { encoding: 'utf8', mode: 0o600 });
92
+ }
93
+ process.stdout.write(` raw events → ${rawPath}\n`);
94
+ }
95
+
96
+ const logger = new Logger({ logDir, agentId, sessionId: sid, silent: true });
97
+ const tracker = new TokenTracker();
98
+
99
+ let count = 0;
100
+ for await (const entry of fetchSessionEntries({ apiKey, agentId, sessionId: sid, model })) {
101
+ const written = await logger.write(entry);
102
+ tracker.record(written);
103
+ count++;
104
+ }
105
+
106
+ const stats = tracker.stats().total;
107
+ const sessionEnd = await logger.write({
108
+ action_type: 'session_end',
109
+ framework: 'anthropic-managed',
110
+ status: 'ok',
111
+ model,
112
+ session_tokens: {
113
+ input: stats.input, output: stats.output,
114
+ cache_read: stats.cache_read, cache_creation: stats.cache_creation,
115
+ total: stats.sum,
116
+ },
117
+ session_cost_usd: stats.cost_usd || null,
118
+ });
119
+
120
+ process.stdout.write(` entries : ${count} (+1 session_end)\n`);
121
+ process.stdout.write(` tokens : in=${stats.input} out=${stats.output} cache_r=${stats.cache_read} cache_w=${stats.cache_creation}\n`);
122
+ process.stdout.write(` written to : ${logger._pathForToday()}\n`);
123
+ totalEntries += count + 1;
124
+ }
125
+
126
+ process.stdout.write(`\n[wma-fetch] done — ${totalEntries} total entries across ${sessions.length} session(s)\n`);
127
+ process.stdout.write(`[wma-fetch] inspect with: npx wma-inspect ${logDir}\n`);
128
+ }
129
+
130
+ main().catch(e => { process.stderr.write(`error: ${e.stack || e.message}\n`); process.exit(1); });