@rubytech/create-realagent 1.0.707 → 1.0.710
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/package.json +1 -1
- package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +53 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.js +132 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -0
- package/payload/platform/lib/mcp-spawn-tee/src/index.ts +134 -0
- package/payload/platform/lib/mcp-spawn-tee/tsconfig.json +8 -0
- package/payload/platform/lib/oauth-llm/dist/index.d.ts +101 -0
- package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/oauth-llm/dist/index.js +353 -0
- package/payload/platform/lib/oauth-llm/dist/index.js.map +1 -0
- package/payload/platform/lib/oauth-llm/src/index.ts +526 -0
- package/payload/platform/lib/oauth-llm/tsconfig.json +8 -0
- package/payload/platform/neo4j/schema.cypher +37 -11
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js +9 -9
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +5 -10
- package/payload/platform/plugins/docs/references/plugins-guide.md +12 -4
- package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts +3 -3
- package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/screening.js +12 -12
- package/payload/platform/plugins/email/mcp/dist/lib/screening.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js +14 -28
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js +9 -19
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +46 -18
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +34 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js +22 -18
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js +4 -4
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +98 -24
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +176 -86
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +12 -46
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +10 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +22 -3
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js +24 -12
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +27 -11
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +276 -238
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/package.json +3 -1
- package/payload/platform/plugins/memory/mcp/scripts/boot-smoke.sh +69 -0
- package/payload/platform/plugins/memory/references/graph-primitives.md +22 -0
- package/payload/platform/plugins/memory/references/schema-base.md +67 -15
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +53 -20
- package/payload/platform/templates/specialists/agents/database-operator.md +18 -0
- package/payload/server/chunk-A5K3CFMI.js +12297 -0
- package/payload/server/chunk-Y57ACANQ.js +12292 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +25 -44
package/package.json
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP spawn-tee — parent-side stderr capture wrapper (Task 743).
|
|
4
|
+
*
|
|
5
|
+
* Claude Code's `--mcp-config` accepts `{command, args, env}` descriptors and
|
|
6
|
+
* spawns each MCP server itself; the platform never holds a ChildProcess
|
|
7
|
+
* handle. The in-process `mcp-stderr-tee` patches `process.stderr.write` from
|
|
8
|
+
* inside the MCP server, but its writes go through `createWriteStream` —
|
|
9
|
+
* async, buffered. A synchronous module-load throw (e.g. schema-loader's
|
|
10
|
+
* line-168 width check on memory) calls `process.exit(1)` before the buffer
|
|
11
|
+
* flushes, so the per-server log file is empty and the platform's
|
|
12
|
+
* `[mcp-init-error] tail="(no stderr file)"` probe is operationally useless.
|
|
13
|
+
* That class shipped as the chronic memory-MCP silent-fail loop fixed by
|
|
14
|
+
* Task 743 (and hit graph in Task 560 — solved there with per-plugin
|
|
15
|
+
* `appendFileSync` discipline that this wrapper now generalises).
|
|
16
|
+
*
|
|
17
|
+
* The wrapper sits between Claude Code and the real MCP server: it spawns
|
|
18
|
+
* the real entry with `stdio: ['inherit', 'inherit', 'pipe']`, then
|
|
19
|
+
* synchronously appends every child stderr chunk to
|
|
20
|
+
* `${LOG_DIR}/mcp-${name}-stderr-<date>.log`. Synchronous writes survive
|
|
21
|
+
* `process.exit` because the kernel queues the syscall before the call
|
|
22
|
+
* returns. The chunk is also written to the wrapper's own stderr so
|
|
23
|
+
* Claude Code's existing stderr consumer is unchanged — the mechanism is
|
|
24
|
+
* additive, not interceptive.
|
|
25
|
+
*
|
|
26
|
+
* Claude Code CLI
|
|
27
|
+
* │ spawns
|
|
28
|
+
* ▼
|
|
29
|
+
* wrapper (this file) — argv[2] = real entry, env.MCP_SPAWN_TEE_NAME = name
|
|
30
|
+
* │ spawns child with stdio:['inherit','inherit','pipe']
|
|
31
|
+
* ▼
|
|
32
|
+
* child = node <real-entry>
|
|
33
|
+
* │
|
|
34
|
+
* ├──▶ stdin/stdout: inherited from wrapper (Claude Code pipe)
|
|
35
|
+
* └──▶ stderr → wrapper.on('data', chunk =>)
|
|
36
|
+
* ├──▶ appendFileSync(${LOG_DIR}/mcp-${name}-stderr-<date>.log)
|
|
37
|
+
* └──▶ process.stderr.write(chunk) → Claude Code consumer
|
|
38
|
+
*
|
|
39
|
+
* The wrapper catches:
|
|
40
|
+
* - MODULE_NOT_FOUND on the entry script itself (success criterion #3:
|
|
41
|
+
* `mv plugins/memory/mcp/dist plugins/memory/mcp/dist-broken && reboot`
|
|
42
|
+
* leaves the cause on disk).
|
|
43
|
+
* - Synchronous throws during module load before the in-process tee runs.
|
|
44
|
+
* - Any stderr a normally-running plugin emits (steady-state telemetry
|
|
45
|
+
* remains visible too — the in-process tee handles per-line stream-log
|
|
46
|
+
* prefixing; this wrapper handles the raw per-server file).
|
|
47
|
+
*
|
|
48
|
+
* SIGTERM/SIGINT propagation: forwarded to the child so a Claude Code
|
|
49
|
+
* shutdown does not orphan the MCP server. Child exit code is propagated
|
|
50
|
+
* verbatim so upstream (Claude Code) sees the real outcome.
|
|
51
|
+
*/
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* MCP spawn-tee — parent-side stderr capture wrapper (Task 743).
|
|
5
|
+
*
|
|
6
|
+
* Claude Code's `--mcp-config` accepts `{command, args, env}` descriptors and
|
|
7
|
+
* spawns each MCP server itself; the platform never holds a ChildProcess
|
|
8
|
+
* handle. The in-process `mcp-stderr-tee` patches `process.stderr.write` from
|
|
9
|
+
* inside the MCP server, but its writes go through `createWriteStream` —
|
|
10
|
+
* async, buffered. A synchronous module-load throw (e.g. schema-loader's
|
|
11
|
+
* line-168 width check on memory) calls `process.exit(1)` before the buffer
|
|
12
|
+
* flushes, so the per-server log file is empty and the platform's
|
|
13
|
+
* `[mcp-init-error] tail="(no stderr file)"` probe is operationally useless.
|
|
14
|
+
* That class shipped as the chronic memory-MCP silent-fail loop fixed by
|
|
15
|
+
* Task 743 (and hit graph in Task 560 — solved there with per-plugin
|
|
16
|
+
* `appendFileSync` discipline that this wrapper now generalises).
|
|
17
|
+
*
|
|
18
|
+
* The wrapper sits between Claude Code and the real MCP server: it spawns
|
|
19
|
+
* the real entry with `stdio: ['inherit', 'inherit', 'pipe']`, then
|
|
20
|
+
* synchronously appends every child stderr chunk to
|
|
21
|
+
* `${LOG_DIR}/mcp-${name}-stderr-<date>.log`. Synchronous writes survive
|
|
22
|
+
* `process.exit` because the kernel queues the syscall before the call
|
|
23
|
+
* returns. The chunk is also written to the wrapper's own stderr so
|
|
24
|
+
* Claude Code's existing stderr consumer is unchanged — the mechanism is
|
|
25
|
+
* additive, not interceptive.
|
|
26
|
+
*
|
|
27
|
+
* Claude Code CLI
|
|
28
|
+
* │ spawns
|
|
29
|
+
* ▼
|
|
30
|
+
* wrapper (this file) — argv[2] = real entry, env.MCP_SPAWN_TEE_NAME = name
|
|
31
|
+
* │ spawns child with stdio:['inherit','inherit','pipe']
|
|
32
|
+
* ▼
|
|
33
|
+
* child = node <real-entry>
|
|
34
|
+
* │
|
|
35
|
+
* ├──▶ stdin/stdout: inherited from wrapper (Claude Code pipe)
|
|
36
|
+
* └──▶ stderr → wrapper.on('data', chunk =>)
|
|
37
|
+
* ├──▶ appendFileSync(${LOG_DIR}/mcp-${name}-stderr-<date>.log)
|
|
38
|
+
* └──▶ process.stderr.write(chunk) → Claude Code consumer
|
|
39
|
+
*
|
|
40
|
+
* The wrapper catches:
|
|
41
|
+
* - MODULE_NOT_FOUND on the entry script itself (success criterion #3:
|
|
42
|
+
* `mv plugins/memory/mcp/dist plugins/memory/mcp/dist-broken && reboot`
|
|
43
|
+
* leaves the cause on disk).
|
|
44
|
+
* - Synchronous throws during module load before the in-process tee runs.
|
|
45
|
+
* - Any stderr a normally-running plugin emits (steady-state telemetry
|
|
46
|
+
* remains visible too — the in-process tee handles per-line stream-log
|
|
47
|
+
* prefixing; this wrapper handles the raw per-server file).
|
|
48
|
+
*
|
|
49
|
+
* SIGTERM/SIGINT propagation: forwarded to the child so a Claude Code
|
|
50
|
+
* shutdown does not orphan the MCP server. Child exit code is propagated
|
|
51
|
+
* verbatim so upstream (Claude Code) sees the real outcome.
|
|
52
|
+
*/
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
const node_child_process_1 = require("node:child_process");
|
|
55
|
+
const node_fs_1 = require("node:fs");
|
|
56
|
+
const node_path_1 = require("node:path");
|
|
57
|
+
const SERVER_NAME = process.env.MCP_SPAWN_TEE_NAME ?? "unknown";
|
|
58
|
+
const LOG_DIR = process.env.LOG_DIR;
|
|
59
|
+
const ENTRY = process.argv[2];
|
|
60
|
+
// Sync-emit: appendFileSync to per-server log + stderr passthrough. Used for
|
|
61
|
+
// wrapper-internal diagnostics (attach line, errors) and for forwarding child
|
|
62
|
+
// stderr chunks. Each destination wrapped independently — an unwritable log
|
|
63
|
+
// must not mask the primary output.
|
|
64
|
+
function syncEmitLine(line) {
|
|
65
|
+
if (LOG_DIR) {
|
|
66
|
+
try {
|
|
67
|
+
(0, node_fs_1.mkdirSync)(LOG_DIR, { recursive: true });
|
|
68
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
69
|
+
(0, node_fs_1.appendFileSync)((0, node_path_1.resolve)(LOG_DIR, `mcp-${SERVER_NAME}-stderr-${date}.log`), line);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
/* unwritable destination — preserve primary failure */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
process.stderr.write(line);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
/* stderr closed — nothing else to do */
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!ENTRY) {
|
|
83
|
+
syncEmitLine(`[mcp-spawn-tee-error] server=${SERVER_NAME} reason="no entry given (argv[2] missing)"\n`);
|
|
84
|
+
process.exit(2);
|
|
85
|
+
}
|
|
86
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [ENTRY], {
|
|
87
|
+
stdio: ["inherit", "inherit", "pipe"],
|
|
88
|
+
env: process.env,
|
|
89
|
+
});
|
|
90
|
+
syncEmitLine(`[mcp-spawn-tee-attached] server=${SERVER_NAME} pid=${child.pid ?? -1} entry=${ENTRY}\n`);
|
|
91
|
+
child.on("error", (err) => {
|
|
92
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
93
|
+
syncEmitLine(`[mcp-spawn-tee-error] server=${SERVER_NAME} reason=${JSON.stringify(`spawn error: ${msg}`)}\n`);
|
|
94
|
+
process.exit(127);
|
|
95
|
+
});
|
|
96
|
+
if (child.stderr) {
|
|
97
|
+
child.stderr.on("data", (chunk) => {
|
|
98
|
+
if (LOG_DIR) {
|
|
99
|
+
try {
|
|
100
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
101
|
+
(0, node_fs_1.appendFileSync)((0, node_path_1.resolve)(LOG_DIR, `mcp-${SERVER_NAME}-stderr-${date}.log`), chunk);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
/* unwritable destination — preserve passthrough */
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
process.stderr.write(chunk);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
/* stderr closed — nothing else to do */
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Forward SIGTERM/SIGINT so Claude Code shutdown doesn't orphan the child.
|
|
116
|
+
const forward = (signal) => {
|
|
117
|
+
if (!child.killed)
|
|
118
|
+
child.kill(signal);
|
|
119
|
+
};
|
|
120
|
+
process.on("SIGTERM", () => forward("SIGTERM"));
|
|
121
|
+
process.on("SIGINT", () => forward("SIGINT"));
|
|
122
|
+
child.on("exit", (code, signal) => {
|
|
123
|
+
if (signal) {
|
|
124
|
+
syncEmitLine(`[mcp-spawn-tee-exit] server=${SERVER_NAME} pid=${child.pid ?? -1} signal=${signal}\n`);
|
|
125
|
+
process.exit(128 + (code ?? 0));
|
|
126
|
+
}
|
|
127
|
+
if (code !== 0) {
|
|
128
|
+
syncEmitLine(`[mcp-spawn-tee-exit] server=${SERVER_NAME} pid=${child.pid ?? -1} code=${code}\n`);
|
|
129
|
+
}
|
|
130
|
+
process.exit(code ?? 0);
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP spawn-tee — parent-side stderr capture wrapper (Task 743).
|
|
4
|
+
*
|
|
5
|
+
* Claude Code's `--mcp-config` accepts `{command, args, env}` descriptors and
|
|
6
|
+
* spawns each MCP server itself; the platform never holds a ChildProcess
|
|
7
|
+
* handle. The in-process `mcp-stderr-tee` patches `process.stderr.write` from
|
|
8
|
+
* inside the MCP server, but its writes go through `createWriteStream` —
|
|
9
|
+
* async, buffered. A synchronous module-load throw (e.g. schema-loader's
|
|
10
|
+
* line-168 width check on memory) calls `process.exit(1)` before the buffer
|
|
11
|
+
* flushes, so the per-server log file is empty and the platform's
|
|
12
|
+
* `[mcp-init-error] tail="(no stderr file)"` probe is operationally useless.
|
|
13
|
+
* That class shipped as the chronic memory-MCP silent-fail loop fixed by
|
|
14
|
+
* Task 743 (and hit graph in Task 560 — solved there with per-plugin
|
|
15
|
+
* `appendFileSync` discipline that this wrapper now generalises).
|
|
16
|
+
*
|
|
17
|
+
* The wrapper sits between Claude Code and the real MCP server: it spawns
|
|
18
|
+
* the real entry with `stdio: ['inherit', 'inherit', 'pipe']`, then
|
|
19
|
+
* synchronously appends every child stderr chunk to
|
|
20
|
+
* `${LOG_DIR}/mcp-${name}-stderr-<date>.log`. Synchronous writes survive
|
|
21
|
+
* `process.exit` because the kernel queues the syscall before the call
|
|
22
|
+
* returns. The chunk is also written to the wrapper's own stderr so
|
|
23
|
+
* Claude Code's existing stderr consumer is unchanged — the mechanism is
|
|
24
|
+
* additive, not interceptive.
|
|
25
|
+
*
|
|
26
|
+
* Claude Code CLI
|
|
27
|
+
* │ spawns
|
|
28
|
+
* ▼
|
|
29
|
+
* wrapper (this file) — argv[2] = real entry, env.MCP_SPAWN_TEE_NAME = name
|
|
30
|
+
* │ spawns child with stdio:['inherit','inherit','pipe']
|
|
31
|
+
* ▼
|
|
32
|
+
* child = node <real-entry>
|
|
33
|
+
* │
|
|
34
|
+
* ├──▶ stdin/stdout: inherited from wrapper (Claude Code pipe)
|
|
35
|
+
* └──▶ stderr → wrapper.on('data', chunk =>)
|
|
36
|
+
* ├──▶ appendFileSync(${LOG_DIR}/mcp-${name}-stderr-<date>.log)
|
|
37
|
+
* └──▶ process.stderr.write(chunk) → Claude Code consumer
|
|
38
|
+
*
|
|
39
|
+
* The wrapper catches:
|
|
40
|
+
* - MODULE_NOT_FOUND on the entry script itself (success criterion #3:
|
|
41
|
+
* `mv plugins/memory/mcp/dist plugins/memory/mcp/dist-broken && reboot`
|
|
42
|
+
* leaves the cause on disk).
|
|
43
|
+
* - Synchronous throws during module load before the in-process tee runs.
|
|
44
|
+
* - Any stderr a normally-running plugin emits (steady-state telemetry
|
|
45
|
+
* remains visible too — the in-process tee handles per-line stream-log
|
|
46
|
+
* prefixing; this wrapper handles the raw per-server file).
|
|
47
|
+
*
|
|
48
|
+
* SIGTERM/SIGINT propagation: forwarded to the child so a Claude Code
|
|
49
|
+
* shutdown does not orphan the MCP server. Child exit code is propagated
|
|
50
|
+
* verbatim so upstream (Claude Code) sees the real outcome.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
import { spawn } from "node:child_process";
|
|
54
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
55
|
+
import { resolve } from "node:path";
|
|
56
|
+
|
|
57
|
+
const SERVER_NAME = process.env.MCP_SPAWN_TEE_NAME ?? "unknown";
|
|
58
|
+
const LOG_DIR = process.env.LOG_DIR;
|
|
59
|
+
const ENTRY = process.argv[2];
|
|
60
|
+
|
|
61
|
+
// Sync-emit: appendFileSync to per-server log + stderr passthrough. Used for
|
|
62
|
+
// wrapper-internal diagnostics (attach line, errors) and for forwarding child
|
|
63
|
+
// stderr chunks. Each destination wrapped independently — an unwritable log
|
|
64
|
+
// must not mask the primary output.
|
|
65
|
+
function syncEmitLine(line: string): void {
|
|
66
|
+
if (LOG_DIR) {
|
|
67
|
+
try {
|
|
68
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
69
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
70
|
+
appendFileSync(resolve(LOG_DIR, `mcp-${SERVER_NAME}-stderr-${date}.log`), line);
|
|
71
|
+
} catch {
|
|
72
|
+
/* unwritable destination — preserve primary failure */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
process.stderr.write(line);
|
|
77
|
+
} catch {
|
|
78
|
+
/* stderr closed — nothing else to do */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!ENTRY) {
|
|
83
|
+
syncEmitLine(`[mcp-spawn-tee-error] server=${SERVER_NAME} reason="no entry given (argv[2] missing)"\n`);
|
|
84
|
+
process.exit(2);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const child = spawn(process.execPath, [ENTRY], {
|
|
88
|
+
stdio: ["inherit", "inherit", "pipe"],
|
|
89
|
+
env: process.env,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
syncEmitLine(`[mcp-spawn-tee-attached] server=${SERVER_NAME} pid=${child.pid ?? -1} entry=${ENTRY}\n`);
|
|
93
|
+
|
|
94
|
+
child.on("error", (err) => {
|
|
95
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
96
|
+
syncEmitLine(`[mcp-spawn-tee-error] server=${SERVER_NAME} reason=${JSON.stringify(`spawn error: ${msg}`)}\n`);
|
|
97
|
+
process.exit(127);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (child.stderr) {
|
|
101
|
+
child.stderr.on("data", (chunk: Buffer) => {
|
|
102
|
+
if (LOG_DIR) {
|
|
103
|
+
try {
|
|
104
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
105
|
+
appendFileSync(resolve(LOG_DIR, `mcp-${SERVER_NAME}-stderr-${date}.log`), chunk);
|
|
106
|
+
} catch {
|
|
107
|
+
/* unwritable destination — preserve passthrough */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
process.stderr.write(chunk);
|
|
112
|
+
} catch {
|
|
113
|
+
/* stderr closed — nothing else to do */
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Forward SIGTERM/SIGINT so Claude Code shutdown doesn't orphan the child.
|
|
119
|
+
const forward = (signal: NodeJS.Signals) => {
|
|
120
|
+
if (!child.killed) child.kill(signal);
|
|
121
|
+
};
|
|
122
|
+
process.on("SIGTERM", () => forward("SIGTERM"));
|
|
123
|
+
process.on("SIGINT", () => forward("SIGINT"));
|
|
124
|
+
|
|
125
|
+
child.on("exit", (code, signal) => {
|
|
126
|
+
if (signal) {
|
|
127
|
+
syncEmitLine(`[mcp-spawn-tee-exit] server=${SERVER_NAME} pid=${child.pid ?? -1} signal=${signal}\n`);
|
|
128
|
+
process.exit(128 + (code ?? 0));
|
|
129
|
+
}
|
|
130
|
+
if (code !== 0) {
|
|
131
|
+
syncEmitLine(`[mcp-spawn-tee-exit] server=${SERVER_NAME} pid=${child.pid ?? -1} code=${code}\n`);
|
|
132
|
+
}
|
|
133
|
+
process.exit(code ?? 0);
|
|
134
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth-bearer LLM calls for admin-side classifiers (Task 740).
|
|
3
|
+
*
|
|
4
|
+
* Every admin-side classifier (memory-classify, memory-rank, commitment,
|
|
5
|
+
* adherence, inbound-gateway, query classifier, summarisation, email
|
|
6
|
+
* screening) uses Claude Code OAuth credentials — never the Anthropic
|
|
7
|
+
* API key. The API-key path is reserved for the public agent.
|
|
8
|
+
*
|
|
9
|
+
* Mechanism: read the access token from ~/.claude/.credentials.json,
|
|
10
|
+
* refresh it via the standard refresh-token grant if expired, then call
|
|
11
|
+
* api.anthropic.com with `Authorization: Bearer <token>` and the
|
|
12
|
+
* `anthropic-beta: oauth-2025-04-20` header. Billed against the operator's
|
|
13
|
+
* Claude Code subscription, not the API-key key. Refresh uses a
|
|
14
|
+
* module-level mutex so concurrent classifier calls during expiry don't
|
|
15
|
+
* race and invalidate each other's tokens (Anthropic rotates both tokens
|
|
16
|
+
* on refresh).
|
|
17
|
+
*
|
|
18
|
+
* Implementation note: this lib uses raw fetch() rather than the
|
|
19
|
+
* @anthropic-ai/sdk package to keep the lib dependency-free — every
|
|
20
|
+
* consumer already has its own SDK copy for other purposes, but the
|
|
21
|
+
* shared lib would create a circular package dependency. The Messages
|
|
22
|
+
* API surface we use (model + system + one user message + max_tokens)
|
|
23
|
+
* is small and stable.
|
|
24
|
+
*
|
|
25
|
+
* Failure modes are surfaced as a structured `Result` so callers can
|
|
26
|
+
* pattern-match without parsing error strings. The skill / agent layer
|
|
27
|
+
* is responsible for translating `fallback` results into operator-visible
|
|
28
|
+
* blocker messages (loud failure doctrine — Task 540, 740).
|
|
29
|
+
*/
|
|
30
|
+
declare const OAUTH_BETA_HEADER = "oauth-2025-04-20";
|
|
31
|
+
/** Anthropic tool definition — structural-output enforcement via function calling. */
|
|
32
|
+
export interface OauthLlmTool {
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
input_schema: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
export interface CallOauthLlmParams {
|
|
38
|
+
/** Anthropic model id (e.g. claude-haiku-4-5). */
|
|
39
|
+
model: string;
|
|
40
|
+
/** System prompt — the classifier instructions. */
|
|
41
|
+
system: string;
|
|
42
|
+
/** User message — the input to classify / rank / etc. */
|
|
43
|
+
userMessage: string;
|
|
44
|
+
/** Max output tokens. Default 4096. */
|
|
45
|
+
maxTokens?: number;
|
|
46
|
+
/** Hard timeout in ms for the model call. Default 60_000. */
|
|
47
|
+
timeoutMs?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Optional tools for structured output. When provided with a forced
|
|
50
|
+
* `toolChoiceName`, the model returns a `tool_use` block whose `input` is
|
|
51
|
+
* a structured object matching the tool's `input_schema` — strictly typed
|
|
52
|
+
* JSON without the parsing brittleness of free-form text output.
|
|
53
|
+
*/
|
|
54
|
+
tools?: OauthLlmTool[];
|
|
55
|
+
/** Force the model to call this tool. Required when `tools` is provided. */
|
|
56
|
+
toolChoiceName?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Result when the call did not declare any tool — only text or fallback. */
|
|
59
|
+
export type CallOauthLlmTextResult = {
|
|
60
|
+
kind: "ok";
|
|
61
|
+
text: string;
|
|
62
|
+
} | CallOauthLlmFallback;
|
|
63
|
+
/** Result when the call forced a tool — only the tool input or fallback. */
|
|
64
|
+
export type CallOauthLlmToolResult = {
|
|
65
|
+
kind: "ok-tool";
|
|
66
|
+
toolName: string;
|
|
67
|
+
input: Record<string, unknown>;
|
|
68
|
+
} | CallOauthLlmFallback;
|
|
69
|
+
/** Discriminated union of every possible result. Returned by the no-overload form. */
|
|
70
|
+
export type CallOauthLlmResult = CallOauthLlmTextResult | CallOauthLlmToolResult;
|
|
71
|
+
export interface CallOauthLlmFallback {
|
|
72
|
+
kind: "fallback";
|
|
73
|
+
/** Human-readable single-line reason (for operator-visible blocker). */
|
|
74
|
+
reason: string;
|
|
75
|
+
/** Stable classifier — callers can pattern-match for retry/abort policy. */
|
|
76
|
+
cause: "missing-creds" | "dead-token" | "refresh-failed" | "auth-error" | "rate-limit" | "server-error" | "network-error" | "timeout" | "empty-response" | "malformed-response" | "unexpected";
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Call an Anthropic model via OAuth bearer auth.
|
|
80
|
+
*
|
|
81
|
+
* Returns:
|
|
82
|
+
* { kind: "ok", text } — model's text response
|
|
83
|
+
* { kind: "ok-tool", toolName, input } — when `tools` + `toolChoiceName` provided
|
|
84
|
+
* { kind: "fallback", reason, cause } — caller decides whether to abort
|
|
85
|
+
*
|
|
86
|
+
* The caller is responsible for translating fallback results into
|
|
87
|
+
* operator-visible blocker messages — this wrapper never silently
|
|
88
|
+
* substitutes a degraded response.
|
|
89
|
+
*
|
|
90
|
+
* Overloads narrow the return type by call shape: text-only callers (no
|
|
91
|
+
* `tools`) statically rule out `ok-tool`; tool callers statically rule out
|
|
92
|
+
* `ok`. Mixed callers get the full union.
|
|
93
|
+
*/
|
|
94
|
+
export declare function callOauthLlm(params: Omit<CallOauthLlmParams, "tools" | "toolChoiceName">): Promise<CallOauthLlmTextResult>;
|
|
95
|
+
export declare function callOauthLlm(params: CallOauthLlmParams & {
|
|
96
|
+
tools: OauthLlmTool[];
|
|
97
|
+
toolChoiceName: string;
|
|
98
|
+
}): Promise<CallOauthLlmToolResult>;
|
|
99
|
+
export declare function callOauthLlm(params: CallOauthLlmParams): Promise<CallOauthLlmResult>;
|
|
100
|
+
export { OAUTH_BETA_HEADER };
|
|
101
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAgBH,QAAA,MAAM,iBAAiB,qBAAqB,CAAC;AAe7C,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,6EAA6E;AAC7E,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5B,oBAAoB,CAAC;AAEzB,4EAA4E;AAC5E,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACrE,oBAAoB,CAAC;AAEzB,sFAAsF;AACtF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG,sBAAsB,CAAC;AAEjF,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,UAAU,CAAC;IACjB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,KAAK,EACD,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,YAAY,GACZ,YAAY,GACZ,cAAc,GACd,eAAe,GACf,SAAS,GACT,gBAAgB,GAChB,oBAAoB,GACpB,YAAY,CAAC;CAClB;AAuND;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,gBAAgB,CAAC,GAC3D,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACnC,wBAAgB,YAAY,CAC1B,MAAM,EAAE,kBAAkB,GAAG;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAC7E,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACnC,wBAAgB,YAAY,CAC1B,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAwK/B,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|