proteum 2.2.9 → 2.3.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 (45) hide show
  1. package/AGENTS.md +3 -2
  2. package/README.md +49 -11
  3. package/agents/project/AGENTS.md +43 -5
  4. package/agents/project/diagnostics.md +6 -2
  5. package/agents/project/optimizations.md +1 -0
  6. package/agents/project/root/AGENTS.md +14 -5
  7. package/agents/project/tests/AGENTS.md +6 -0
  8. package/agents/project/tests/e2e/AGENTS.md +13 -0
  9. package/agents/project/tests/e2e/REAL_WORLD_JOURNEY_TESTS.md +192 -0
  10. package/cli/commands/connect.ts +40 -4
  11. package/cli/commands/diagnose.ts +136 -5
  12. package/cli/commands/doctor.ts +24 -4
  13. package/cli/commands/explain.ts +105 -6
  14. package/cli/commands/mcp.ts +16 -0
  15. package/cli/commands/orient.ts +66 -3
  16. package/cli/commands/perf.ts +118 -13
  17. package/cli/commands/runtime.ts +151 -0
  18. package/cli/commands/trace.ts +116 -21
  19. package/cli/mcp/provider.ts +365 -0
  20. package/cli/mcp/stdio.ts +16 -0
  21. package/cli/presentation/commands.ts +77 -20
  22. package/cli/presentation/devSession.ts +2 -0
  23. package/cli/runtime/commands.ts +95 -12
  24. package/cli/utils/agentOutput.ts +46 -0
  25. package/cli/utils/agents.ts +116 -49
  26. package/common/dev/inspection.ts +14 -6
  27. package/common/dev/mcpPayloads.ts +736 -0
  28. package/common/dev/mcpServer.ts +254 -0
  29. package/docs/agent-routing.md +126 -0
  30. package/docs/dev-commands.md +2 -0
  31. package/docs/dev-sessions.md +2 -1
  32. package/docs/diagnostics.md +68 -23
  33. package/docs/mcp.md +149 -0
  34. package/docs/migrate-from-2.1.3.md +15 -5
  35. package/docs/request-tracing.md +12 -6
  36. package/package.json +2 -1
  37. package/server/app/devMcp.ts +159 -0
  38. package/server/services/router/http/cache.ts +116 -0
  39. package/server/services/router/http/index.ts +94 -35
  40. package/server/services/router/index.ts +8 -11
  41. package/tests/agents-utils.test.cjs +36 -13
  42. package/tests/dev-transpile-watch.test.cjs +117 -8
  43. package/tests/inspection.test.cjs +67 -0
  44. package/tests/mcp.test.cjs +127 -0
  45. package/tests/router-cache-config.test.cjs +74 -0
@@ -0,0 +1,127 @@
1
+ const assert = require('node:assert/strict');
2
+ const fs = require('node:fs');
3
+ const os = require('node:os');
4
+ const path = require('node:path');
5
+ const test = require('node:test');
6
+
7
+ const coreRoot = path.resolve(__dirname, '..');
8
+ process.env.TS_NODE_PROJECT = path.join(coreRoot, 'cli', 'tsconfig.json');
9
+ process.env.TS_NODE_TRANSPILE_ONLY = '1';
10
+ require('ts-node/register/transpile-only');
11
+ require('../cli/context.ts');
12
+
13
+ const { Client } = require('@modelcontextprotocol/sdk/client/index.js');
14
+ const { InMemoryTransport } = require('@modelcontextprotocol/sdk/inMemory.js');
15
+ const { createMcpPayload, compactTraceResponse, resolveInstructionRouting } = require('../common/dev/mcpPayloads.ts');
16
+ const { createProteumMcpServer } = require('../common/dev/mcpServer.ts');
17
+
18
+ const writeFile = (filepath, content) => {
19
+ fs.mkdirSync(path.dirname(filepath), { recursive: true });
20
+ fs.writeFileSync(filepath, content);
21
+ };
22
+
23
+ test('instruction routing returns compact selected files for a page query', () => {
24
+ const appRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'proteum-mcp-app-'));
25
+
26
+ writeFile(path.join(appRoot, 'AGENTS.md'), '# App Agents\n\n- root\n');
27
+ writeFile(path.join(appRoot, 'client', 'AGENTS.md'), '# Client Agents\n\n- client\n');
28
+ writeFile(path.join(appRoot, 'client', 'pages', 'AGENTS.md'), '# Page Agents\n\n- pages\n');
29
+ writeFile(path.join(appRoot, 'diagnostics.md'), '# Diagnostics\n\n- diagnose\n');
30
+
31
+ const payload = resolveInstructionRouting({ appRoot, query: '/domains/:slug client/pages/domain.tsx' });
32
+
33
+ assert.equal(payload.ok, true);
34
+ assert.equal(payload.format, 'proteum-mcp-v1');
35
+ assert.deepEqual(
36
+ payload.data.selected.map((entry) => path.relative(appRoot, entry.file)).sort(),
37
+ ['AGENTS.md', 'client/AGENTS.md', 'client/pages/AGENTS.md'],
38
+ );
39
+ assert.equal(payload.data.readWhen.some((entry) => entry.file && entry.file.endsWith('diagnostics.md')), true);
40
+ });
41
+
42
+ test('trace payload keeps default output compact and paginates full details', () => {
43
+ const request = {
44
+ id: 'req_1',
45
+ method: 'GET',
46
+ path: '/domains',
47
+ url: 'http://localhost:3000/domains',
48
+ capture: 'deep',
49
+ startedAt: new Date().toISOString(),
50
+ finishedAt: new Date().toISOString(),
51
+ durationMs: 42,
52
+ statusCode: 200,
53
+ droppedEvents: 0,
54
+ calls: Array.from({ length: 3 }, (_, index) => ({
55
+ id: `call_${index}`,
56
+ origin: 'server',
57
+ label: `Call ${index}`,
58
+ method: 'POST',
59
+ path: `/api/${index}`,
60
+ statusCode: 200,
61
+ durationMs: 10 + index,
62
+ requestDataKeys: [],
63
+ resultKeys: [],
64
+ })),
65
+ events: Array.from({ length: 12 }, (_, index) => ({
66
+ index,
67
+ elapsedMs: index,
68
+ type: index === 2 ? 'error' : 'mark',
69
+ details: { index, long: 'x'.repeat(300) },
70
+ })),
71
+ sqlQueries: Array.from({ length: 4 }, (_, index) => ({
72
+ id: `sql_${index}`,
73
+ callerLabel: 'Service.query',
74
+ callerMethod: 'query',
75
+ callerPath: 'server/services/Domain/index.ts',
76
+ kind: 'query',
77
+ operation: 'findMany',
78
+ model: 'Domain',
79
+ durationMs: index + 1,
80
+ fingerprint: `fp_${index}`,
81
+ query: 'select * from Domain where id = ?',
82
+ })),
83
+ };
84
+
85
+ const compact = compactTraceResponse({ request });
86
+ const full = compactTraceResponse({ detail: 'full', limit: 5, offset: 4, request });
87
+
88
+ assert.equal(compact.data.page, undefined);
89
+ assert.equal(compact.omitted.length, 1);
90
+ assert.equal(full.data.page.events.length, 5);
91
+ assert.equal(full.data.page.hasMore, true);
92
+ });
93
+
94
+ test('MCP server registers the Proteum read-only tool contract', async () => {
95
+ const payload = createMcpPayload({ summary: 'ok', data: { value: 1 } });
96
+ const provider = {
97
+ diagnose: async () => payload,
98
+ doctor: async () => payload,
99
+ explainSummary: async () => payload,
100
+ instructionsResolve: async () => payload,
101
+ logsTail: async () => payload,
102
+ orient: async () => payload,
103
+ perfRequest: async () => payload,
104
+ perfTop: async () => payload,
105
+ readResource: async () => payload,
106
+ runtimeStatus: async () => payload,
107
+ traceLatest: async () => payload,
108
+ traceShow: async () => payload,
109
+ };
110
+ const server = createProteumMcpServer({ provider, version: 'test' });
111
+ const client = new Client({ name: 'mcp-test', version: '1.0.0' });
112
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
113
+
114
+ await server.connect(serverTransport);
115
+ await client.connect(clientTransport);
116
+
117
+ const tools = await client.listTools();
118
+ const result = await client.callTool({ name: 'runtime_status', arguments: {} });
119
+ const resource = await client.readResource({ uri: 'proteum://runtime/status' });
120
+
121
+ assert.equal(tools.tools.some((tool) => tool.name === 'runtime_status'), true);
122
+ assert.match(result.content[0].text, /proteum-mcp-v1/);
123
+ assert.match(resource.contents[0].text, /proteum-mcp-v1/);
124
+
125
+ await client.close();
126
+ await server.close();
127
+ });
@@ -0,0 +1,74 @@
1
+ const assert = require('node:assert/strict');
2
+ const test = require('node:test');
3
+
4
+ require('ts-node/register/transpile-only');
5
+
6
+ const {
7
+ resolveHttpCacheConfig,
8
+ resolvePublicAssetCacheControl,
9
+ } = require('../server/services/router/http/cache');
10
+
11
+ test('router cache config preserves current defaults', () => {
12
+ const cache = resolveHttpCacheConfig();
13
+
14
+ assert.equal(cache.html.dynamic.cacheControl, 'no-store, no-cache, must-revalidate, proxy-revalidate');
15
+ assert.equal(cache.html.dynamic.surrogateControl, 'no-store');
16
+ assert.equal(cache.html.static.cacheControl, 'public, max-age=0, must-revalidate');
17
+ assert.equal(cache.html.static.surrogateControl, false);
18
+ assert.equal(cache.publicAssets.dev, 'no-store');
19
+ assert.equal(cache.publicAssets.versioned, 'public, max-age=31536000, immutable');
20
+ assert.equal(cache.publicAssets.unversioned, 'public, max-age=0, must-revalidate');
21
+ assert.equal(cache.publicAssets.etag, undefined);
22
+ assert.equal(cache.publicAssets.lastModified, undefined);
23
+ });
24
+
25
+ test('router cache config resolves granular public asset policies', () => {
26
+ const cache = resolveHttpCacheConfig({
27
+ publicAssets: {
28
+ dev: 'dev-cache',
29
+ versioned: 'versioned-cache',
30
+ unversioned: 'unversioned-cache',
31
+ etag: false,
32
+ lastModified: false,
33
+ },
34
+ });
35
+
36
+ assert.equal(cache.publicAssets.etag, false);
37
+ assert.equal(cache.publicAssets.lastModified, false);
38
+ assert.equal(
39
+ resolvePublicAssetCacheControl({
40
+ res: undefined,
41
+ filePath: '/app/public/client.abc123.js',
42
+ profile: 'prod',
43
+ cache: cache.publicAssets,
44
+ }),
45
+ 'versioned-cache',
46
+ );
47
+ assert.equal(
48
+ resolvePublicAssetCacheControl({
49
+ res: { req: { originalUrl: '/public/client.js?v=123' } },
50
+ filePath: '/app/public/client.js',
51
+ profile: 'prod',
52
+ cache: cache.publicAssets,
53
+ }),
54
+ 'versioned-cache',
55
+ );
56
+ assert.equal(
57
+ resolvePublicAssetCacheControl({
58
+ res: { req: { originalUrl: '/public/client.js' } },
59
+ filePath: '/app/public/client.js',
60
+ profile: 'prod',
61
+ cache: cache.publicAssets,
62
+ }),
63
+ 'unversioned-cache',
64
+ );
65
+ assert.equal(
66
+ resolvePublicAssetCacheControl({
67
+ res: { req: { originalUrl: '/public/client.abc123.js' } },
68
+ filePath: '/app/public/client.abc123.js',
69
+ profile: 'dev',
70
+ cache: cache.publicAssets,
71
+ }),
72
+ 'dev-cache',
73
+ );
74
+ });