@noesis-brain/mcp-server 2.1.3 → 2.3.1
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/dist/agent/identity.d.ts +33 -0
- package/dist/agent/identity.d.ts.map +1 -0
- package/dist/agent/identity.js +61 -0
- package/dist/agent/identity.js.map +1 -0
- package/dist/agent/runner.d.ts +194 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +465 -50
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/tokenState.d.ts +81 -0
- package/dist/agent/tokenState.d.ts.map +1 -0
- package/dist/agent/tokenState.js +173 -0
- package/dist/agent/tokenState.js.map +1 -0
- package/dist/agent/webGuard.d.ts +48 -0
- package/dist/agent/webGuard.d.ts.map +1 -0
- package/dist/agent/webGuard.js +173 -0
- package/dist/agent/webGuard.js.map +1 -0
- package/dist/api/NoesisClient.d.ts +13 -1
- package/dist/api/NoesisClient.d.ts.map +1 -1
- package/dist/api/NoesisClient.js +18 -3
- package/dist/api/NoesisClient.js.map +1 -1
- package/dist/tools/claudeSessions.d.ts +72 -0
- package/dist/tools/claudeSessions.d.ts.map +1 -0
- package/dist/tools/claudeSessions.js +304 -0
- package/dist/tools/claudeSessions.js.map +1 -0
- package/dist/tools/index.d.ts +1 -6
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +188 -55
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/sessionIndex.d.ts +66 -0
- package/dist/tools/sessionIndex.d.ts.map +1 -0
- package/dist/tools/sessionIndex.js +241 -0
- package/dist/tools/sessionIndex.js.map +1 -0
- package/package.json +1 -1
- package/scripts/noesis-capture-watcher.mjs +722 -107
- package/scripts/start-agent.sh +331 -0
- package/skill-templates/noesis-refine-relations.md +163 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which machine, and which process, a daemon is — sent with every claim.
|
|
3
|
+
*
|
|
4
|
+
* A user may run the agent on several computers. The server keeps one health record
|
|
5
|
+
* per machine (online? over its Claude limit? logged out?) so a healthy Mac can take
|
|
6
|
+
* the jobs a blocked Windows box should not, and so the right machine's status shows
|
|
7
|
+
* in Settings. Older daemons sent nothing that identifies the machine, so the server
|
|
8
|
+
* fell back to the API token — two machines sharing one token looked like one.
|
|
9
|
+
*
|
|
10
|
+
* - daemonId: stable across restarts. Read from (or created in) `<run dir>/daemon-id`;
|
|
11
|
+
* `NOESIS_AGENT_ID` overrides it (tests, or several daemons deliberately run apart).
|
|
12
|
+
* - bootId: new for every process, so the server can tell a restart (which clears a
|
|
13
|
+
* stale block) from two processes running on the same machine.
|
|
14
|
+
* - label / platform: shown to the user to tell machines apart.
|
|
15
|
+
*/
|
|
16
|
+
export interface DaemonIdentity {
|
|
17
|
+
daemonId: string;
|
|
18
|
+
bootId: string;
|
|
19
|
+
label: string;
|
|
20
|
+
platform: string;
|
|
21
|
+
}
|
|
22
|
+
type Env = Record<string, string | undefined>;
|
|
23
|
+
/** Where the daemon keeps its state — the same directory scripts/start-agent.sh runs it in. */
|
|
24
|
+
export declare function agentRunDir(env?: Env): string;
|
|
25
|
+
/**
|
|
26
|
+
* The stable machine id. If the file can be neither read nor written, a hash of
|
|
27
|
+
* machine facts keeps restarts agreeing with each other instead of minting a new
|
|
28
|
+
* "machine" every time.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveDaemonId(env?: Env): string;
|
|
31
|
+
export declare function resolveIdentity(env?: Env): DaemonIdentity;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/agent/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAQ9C,+FAA+F;AAC/F,wBAAgB,WAAW,CAAC,GAAG,GAAE,GAAiB,GAAG,MAAM,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,GAAiB,GAAG,MAAM,CAoB9D;AAED,wBAAgB,eAAe,CAAC,GAAG,GAAE,GAAiB,GAAG,cAAc,CAGtE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which machine, and which process, a daemon is — sent with every claim.
|
|
3
|
+
*
|
|
4
|
+
* A user may run the agent on several computers. The server keeps one health record
|
|
5
|
+
* per machine (online? over its Claude limit? logged out?) so a healthy Mac can take
|
|
6
|
+
* the jobs a blocked Windows box should not, and so the right machine's status shows
|
|
7
|
+
* in Settings. Older daemons sent nothing that identifies the machine, so the server
|
|
8
|
+
* fell back to the API token — two machines sharing one token looked like one.
|
|
9
|
+
*
|
|
10
|
+
* - daemonId: stable across restarts. Read from (or created in) `<run dir>/daemon-id`;
|
|
11
|
+
* `NOESIS_AGENT_ID` overrides it (tests, or several daemons deliberately run apart).
|
|
12
|
+
* - bootId: new for every process, so the server can tell a restart (which clears a
|
|
13
|
+
* stale block) from two processes running on the same machine.
|
|
14
|
+
* - label / platform: shown to the user to tell machines apart.
|
|
15
|
+
*/
|
|
16
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
17
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import { homedir, hostname, platform } from 'node:os';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
const ID_PATTERN = /^[A-Za-z0-9._:-]{1,80}$/;
|
|
21
|
+
function validId(v) {
|
|
22
|
+
return !!v && ID_PATTERN.test(v) && !/^legacy:/i.test(v);
|
|
23
|
+
}
|
|
24
|
+
/** Where the daemon keeps its state — the same directory scripts/start-agent.sh runs it in. */
|
|
25
|
+
export function agentRunDir(env = process.env) {
|
|
26
|
+
return env.NOESIS_AGENT_RUN_DIR || join(homedir(), '.noesis-agent');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The stable machine id. If the file can be neither read nor written, a hash of
|
|
30
|
+
* machine facts keeps restarts agreeing with each other instead of minting a new
|
|
31
|
+
* "machine" every time.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveDaemonId(env = process.env) {
|
|
34
|
+
const override = env.NOESIS_AGENT_ID?.trim();
|
|
35
|
+
if (validId(override))
|
|
36
|
+
return override;
|
|
37
|
+
const dir = agentRunDir(env);
|
|
38
|
+
const file = join(dir, 'daemon-id');
|
|
39
|
+
try {
|
|
40
|
+
const existing = readFileSync(file, 'utf8').trim();
|
|
41
|
+
if (validId(existing))
|
|
42
|
+
return existing;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
/* not created yet */
|
|
46
|
+
}
|
|
47
|
+
const id = randomUUID();
|
|
48
|
+
try {
|
|
49
|
+
mkdirSync(dir, { recursive: true });
|
|
50
|
+
writeFileSync(file, `${id}\n`, { mode: 0o600 });
|
|
51
|
+
return id;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return `h-${createHash('sha256').update(`${hostname()}|${platform()}|${homedir()}`).digest('hex').slice(0, 32)}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function resolveIdentity(env = process.env) {
|
|
58
|
+
const label = (env.NOESIS_AGENT_LABEL?.trim() || hostname() || 'agent').slice(0, 80);
|
|
59
|
+
return { daemonId: resolveDaemonId(env), bootId: randomUUID(), label, platform: platform() };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/agent/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAWjC,MAAM,UAAU,GAAG,yBAAyB,CAAC;AAE7C,SAAS,OAAO,CAAC,CAAqB;IACpC,OAAO,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,WAAW,CAAC,MAAW,OAAO,CAAC,GAAG;IAChD,OAAO,GAAG,CAAC,oBAAoB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAW,OAAO,CAAC,GAAG;IACpD,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC7C,IAAI,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEvC,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,IAAI,OAAO,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,qBAAqB;IACvB,CAAC;IACD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,IAAI,CAAC;QACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,QAAQ,EAAE,IAAI,QAAQ,EAAE,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACnH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAW,OAAO,CAAC,GAAG;IACpD,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,QAAQ,EAAE,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrF,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC;AAC/F,CAAC"}
|
package/dist/agent/runner.d.ts
CHANGED
|
@@ -13,15 +13,47 @@
|
|
|
13
13
|
* FOOTGUN GUARD: if ANTHROPIC_API_KEY is set, the Agent SDK bills the metered API
|
|
14
14
|
* instead of the subscription — so we delete it (and warn) before any query().
|
|
15
15
|
*/
|
|
16
|
+
import { type UrlVerdict } from './webGuard.js';
|
|
17
|
+
import { type DaemonIdentity } from './identity.js';
|
|
18
|
+
import { TokenGate } from './tokenState.js';
|
|
19
|
+
/**
|
|
20
|
+
* NOESIS_AGENT_FAKE: '1' (or 'canned') answers every job with canned text; 'rate_limit'
|
|
21
|
+
* and 'auth' replay what the Claude Agent SDK emits on a subscription usage limit / a
|
|
22
|
+
* revoked login, through the SAME stream handling as a real run, until
|
|
23
|
+
* NOESIS_AGENT_FAKE_RESET_MIN minutes after start — then they answer like 'canned'.
|
|
24
|
+
* For testing the server's fallback and recovery without spending real quota.
|
|
25
|
+
*/
|
|
26
|
+
export type FakeMode = 'canned' | 'rate_limit' | 'auth';
|
|
16
27
|
export interface AgentConfig {
|
|
17
28
|
apiBaseUrl: string;
|
|
18
29
|
apiToken: string;
|
|
19
30
|
concurrency: number;
|
|
20
31
|
fake: boolean;
|
|
32
|
+
fakeMode?: FakeMode;
|
|
33
|
+
fakeResetAt?: number;
|
|
34
|
+
fakeWordMs?: number;
|
|
21
35
|
}
|
|
22
36
|
export declare function resolveAgentConfig(): AgentConfig;
|
|
37
|
+
interface ClaimedJob {
|
|
38
|
+
jobId: string;
|
|
39
|
+
kind: string;
|
|
40
|
+
payload: JobPayload;
|
|
41
|
+
lease: string;
|
|
42
|
+
}
|
|
43
|
+
interface JobPayload {
|
|
44
|
+
prompt?: string;
|
|
45
|
+
system?: string;
|
|
46
|
+
allowedTools?: string[];
|
|
47
|
+
maxTurns?: number;
|
|
48
|
+
images?: Array<{
|
|
49
|
+
mimeType: string;
|
|
50
|
+
data: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
23
53
|
/** Drop anything the server asked for that is not on the read-only allowlist. */
|
|
24
54
|
export declare function clampAllowedTools(raw: unknown): string[];
|
|
55
|
+
/** The web built-ins this job asked for, in a fixed order. Nothing else can come out. */
|
|
56
|
+
export declare function clampWebTools(raw: unknown): string[];
|
|
25
57
|
/** Clamp the server-requested turn budget into a sane range. */
|
|
26
58
|
export declare function clampMaxTurns(raw: unknown): number;
|
|
27
59
|
/**
|
|
@@ -30,6 +62,19 @@ export declare function clampMaxTurns(raw: unknown): number;
|
|
|
30
62
|
* package.json rather than hard-coded, so a release cannot forget to bump it.
|
|
31
63
|
*/
|
|
32
64
|
export declare function agentVersion(): string;
|
|
65
|
+
/**
|
|
66
|
+
* What every claim sends. The server keeps one health record per machine (daemonId), tells
|
|
67
|
+
* a restart from a second process by bootId, and shows label/platform in Settings. Older
|
|
68
|
+
* servers read only workerId + agentVersion and ignore the rest.
|
|
69
|
+
*/
|
|
70
|
+
export declare function claimBody(workerId: string, id: DaemonIdentity): Record<string, unknown>;
|
|
71
|
+
/**
|
|
72
|
+
* Read a claim answer. A 401 means the server refused this daemon's API token (expired,
|
|
73
|
+
* revoked or unknown): the shared gate explains it once and the workers back off, instead
|
|
74
|
+
* of retrying silently every 2 s. Any accepted answer clears that state and feeds the
|
|
75
|
+
* token-expiry warning.
|
|
76
|
+
*/
|
|
77
|
+
export declare function handleClaimResponse(res: Response, gate: TokenGate): Promise<ClaimedJob | null>;
|
|
33
78
|
/**
|
|
34
79
|
* One Anthropic content block — a plain-text piece or a base64 image, the same
|
|
35
80
|
* shape the SSE path's formatClaudeContent() already builds (main repo,
|
|
@@ -74,6 +119,155 @@ export declare function buildSdkPrompt(prompt: string, images: Array<{
|
|
|
74
119
|
mimeType: string;
|
|
75
120
|
data: string;
|
|
76
121
|
}> | undefined): string | AsyncGenerator<unknown>;
|
|
122
|
+
/** What the SDK expects back from a canUseTool decision. */
|
|
123
|
+
type PermissionDecision = {
|
|
124
|
+
behavior: 'allow';
|
|
125
|
+
updatedInput: Record<string, unknown>;
|
|
126
|
+
} | {
|
|
127
|
+
behavior: 'deny';
|
|
128
|
+
message: string;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* SECOND, INDEPENDENT LAYER on the tool boundary.
|
|
132
|
+
*
|
|
133
|
+
* The `tools` option below (`[]`, or at most the two web tools) is the primary control, but
|
|
134
|
+
* it works by way of the CLI's `--tools` flag, whose handling lives in a minified `cli.js`
|
|
135
|
+
* we cannot audit. This callback is a
|
|
136
|
+
* gate we own outright: the SDK routes every permission request through it
|
|
137
|
+
* (`--permission-prompt-tool stdio`), and the request carries an `agentID`, so calls made
|
|
138
|
+
* by a spawned `Task` SUBAGENT are covered too — which matters, because a subagent, not
|
|
139
|
+
* the main loop, did the file reading in the 2026-08-14 incident.
|
|
140
|
+
*
|
|
141
|
+
* Deliberately fed from ALLOWED_TOOLS / ALLOWED_WEB_TOOLS (via the per-job clamps) so the
|
|
142
|
+
* daemon has ONE definition of what may run.
|
|
143
|
+
*
|
|
144
|
+
* DO NOT DELETE THIS AS DEAD CODE. It is the ONLY daemon-owned control over MCP tools.
|
|
145
|
+
* `tools: []` governs the SDK's BUILT-IN set and nothing else; the Noesis MCP server we
|
|
146
|
+
* spawn registers ~55 tools, and their definitions are not filtered by `allowedTools`, so
|
|
147
|
+
* the model can see and attempt every one of them — including `pull_notes`, which writes
|
|
148
|
+
* to an arbitrary local path, plus `add_root`, `sync_notes`, `trash_note`, `move_note`.
|
|
149
|
+
* For those, this callback is the whole boundary.
|
|
150
|
+
*
|
|
151
|
+
* MEASURED (30 runs, 2026-08-15), correcting an earlier comment that said the opposite:
|
|
152
|
+
* the gate DOES fire — it denied `mcp__noesis__list_roots` and `mcp__noesis__list_codebases`
|
|
153
|
+
* in real traffic, and the model received `is_error` results naming the refusal. An earlier
|
|
154
|
+
* 2-job sample saw zero firings and wrongly concluded the gate was inert; a permission tool
|
|
155
|
+
* is simply not consulted for calls that are already pre-approved, and that sample happened
|
|
156
|
+
* to exercise only pre-approved tools.
|
|
157
|
+
*
|
|
158
|
+
* KNOWN GAP, stated rather than glossed: the SDK's cache-warming sidechain
|
|
159
|
+
* (`isSidechain: true`) is a real model turn that emits genuine `Bash`/`Glob` calls against
|
|
160
|
+
* the daemon's cwd, with only the RESULT stubbed. `tools: []` does not reach it and this
|
|
161
|
+
* callback is never consulted there. Nothing executes — but that is Claude Code internal
|
|
162
|
+
* behaviour we neither own nor test, which is why the daemon is rooted at a neutral cwd
|
|
163
|
+
* (`scripts/start-agent.sh`), so the warmup probes an empty directory, not a source tree.
|
|
164
|
+
*
|
|
165
|
+
* WEB TOOLS: a permitted WebFetch is still refused unless its URL passes webGuard (public
|
|
166
|
+
* http(s) host only). The job's permitted set is checked FIRST, so a job never cleared
|
|
167
|
+
* for web cannot fetch even a public page. WebSearch needs no address check: the search
|
|
168
|
+
* runs on Anthropic's side, not from this machine. One exception the gate cannot see: the
|
|
169
|
+
* CLI itself pre-approves ~85 public documentation hosts for WebFetch and never asks. Those
|
|
170
|
+
* are public by definition, and executeJob logs every tool call from the stream anyway.
|
|
171
|
+
*/
|
|
172
|
+
export declare function buildCanUseTool(
|
|
173
|
+
/**
|
|
174
|
+
* The tools permitted for THIS job — i.e. the output of `clampAllowedTools`, not the
|
|
175
|
+
* module-wide `ALLOWED_TOOLS`. Required, deliberately: an earlier version closed over
|
|
176
|
+
* `ALLOWED_TOOLS` and so turned a per-job clamp into a union. `clampAllowedTools`
|
|
177
|
+
* narrows to (what the server asked for ∩ ALLOWED_TOOLS); the gate then re-allowed all
|
|
178
|
+
* four regardless, so a job clamped to `get_note` could still execute `search_semantic`.
|
|
179
|
+
* The sets happen to coincide for chat today, which is exactly why it would have gone
|
|
180
|
+
* unnoticed until the first job kind with a narrower allowlist.
|
|
181
|
+
*/
|
|
182
|
+
permitted: Iterable<string>, onDecision?: (toolName: string, allowed: boolean, agentID?: string, detail?: string) => void, checkUrl?: (url: unknown) => Promise<UrlVerdict>): (toolName: string, input?: unknown, ctx?: {
|
|
183
|
+
agentID?: string;
|
|
184
|
+
}) => Promise<PermissionDecision>;
|
|
185
|
+
/**
|
|
186
|
+
* The `options` object handed to sdk.query() — extracted, like buildSdkPrompt above, so
|
|
187
|
+
* the tool boundary is asserted by a test instead of resting on a comment. `tools` is the
|
|
188
|
+
* security-relevant field: see the note at its assignment. Takes the MCP-server
|
|
189
|
+
* factory as an argument so a test can supply a stub instead of a real spawn config.
|
|
190
|
+
*/
|
|
191
|
+
export declare function buildQueryOptions(payload: JobPayload, mcpServersFor: () => Record<string, unknown>): Record<string, unknown>;
|
|
192
|
+
/** The prompt text for a job, with the fallback used when a payload carries none. */
|
|
193
|
+
export declare function resolveJobPrompt(payload: JobPayload, kindLabel: string): string;
|
|
194
|
+
/** The minimum of the Agent SDK this daemon uses — enough for a test to substitute a fake. */
|
|
195
|
+
export interface SdkLike {
|
|
196
|
+
query(args: {
|
|
197
|
+
prompt: unknown;
|
|
198
|
+
options: Record<string, unknown>;
|
|
199
|
+
}): AsyncIterable<unknown>;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The ONE place the SDK is invoked. Exported so a test can pass a fake `sdk` and assert
|
|
203
|
+
* what actually reaches `query()`.
|
|
204
|
+
*
|
|
205
|
+
* This exists because of a real gap: the suite pinned `buildQueryOptions` with a
|
|
206
|
+
* power-set mutation battery, but NOTHING pinned the call. Restoring the pre-fix inline
|
|
207
|
+
* options literal here — deleting `tools: []` and `canUseTool` outright — reverted the
|
|
208
|
+
* entire tool boundary with all 79 tests still green. The builder being correct is
|
|
209
|
+
* worthless if the caller stops using it, so both the seam below and the source-fitness
|
|
210
|
+
* test in `agentToolClamp.test.ts` guard this.
|
|
211
|
+
*/
|
|
212
|
+
export declare function runSdkQuery(sdk: SdkLike, payload: JobPayload, kindLabel: string, mcpServersFor: () => Record<string, unknown>): AsyncIterable<unknown>;
|
|
213
|
+
/**
|
|
214
|
+
* Appended when the SDK stops a run at maxTurns. Without it the saved reply would be only
|
|
215
|
+
* the narration that came before the tool calls ("let me check the official page…"), with
|
|
216
|
+
* nothing saying the answer was cut off.
|
|
217
|
+
*/
|
|
218
|
+
export declare const TURN_LIMIT_NOTE = "\n\n_(Stopped at the step limit before finishing. Ask me to continue.)_";
|
|
219
|
+
/**
|
|
220
|
+
* Turn one SDK message stream into the reply text, pushing it to `out` as it arrives.
|
|
221
|
+
* Extracted from executeJob so these rules are testable against a fake stream:
|
|
222
|
+
* - With includePartialMessages the stream carries BOTH incremental `stream_event` deltas
|
|
223
|
+
* AND a terminal `assistant` message holding the whole reply. Taking both would emit the
|
|
224
|
+
* answer twice, so deltas win and the terminal message is only a fallback for a stream
|
|
225
|
+
* that produced none (older SDK, or partials unsupported).
|
|
226
|
+
* - A multi-turn run (every web fetch or note lookup) gets a paragraph break between turns,
|
|
227
|
+
* so narration does not run into the answer. Only when the next turn actually has text,
|
|
228
|
+
* so a tool-only turn adds nothing.
|
|
229
|
+
* - Every tool call is logged, including WebFetch calls to the hosts the CLI pre-approves
|
|
230
|
+
* itself, which never reach canUseTool.
|
|
231
|
+
* - Hitting maxTurns appends TURN_LIMIT_NOTE.
|
|
232
|
+
*/
|
|
233
|
+
export declare function consumeAgentStream(stream: AsyncIterable<unknown>, out: {
|
|
234
|
+
push(text: string): void;
|
|
235
|
+
isClosed(): boolean;
|
|
236
|
+
}, logToolCall?: (name: string, detail?: string) => void): Promise<string>;
|
|
237
|
+
/** A Claude API failure the daemon reports with its own text and SDK error code. */
|
|
238
|
+
export declare class AgentRunError extends Error {
|
|
239
|
+
readonly code: string;
|
|
240
|
+
readonly resetsAt?: number | undefined;
|
|
241
|
+
constructor(message: string, code: string, resetsAt?: number | undefined);
|
|
242
|
+
}
|
|
243
|
+
/** The /complete body for a failed job: the message, plus the SDK's code when known. */
|
|
244
|
+
export declare function completionErrorBody(lease: string, err: unknown): Record<string, unknown>;
|
|
245
|
+
export interface FakeSdkOptions {
|
|
246
|
+
kind: string;
|
|
247
|
+
promptText: string;
|
|
248
|
+
/** Epoch ms after which rate_limit/auth answer like 'canned'. */
|
|
249
|
+
resetAt: number;
|
|
250
|
+
wordMs: number;
|
|
251
|
+
/** End a failing run by throwing, as the real SDK does when the CLI exits 1 (default true). */
|
|
252
|
+
throwAtEnd?: boolean;
|
|
253
|
+
now?: () => number;
|
|
254
|
+
timeZone?: string;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* A stand-in for the Agent SDK that emits the message sequences a real run does, so the
|
|
258
|
+
* fake modes exercise consumeAgentStream exactly like real traffic.
|
|
259
|
+
*/
|
|
260
|
+
export declare function fakeSdk(mode: FakeMode, opts: FakeSdkOptions): SdkLike;
|
|
261
|
+
/**
|
|
262
|
+
* A reset time the way the Claude CLI prints it in its limit message: "3pm (Zone)",
|
|
263
|
+
* "3:07pm (Zone)", or "Oct 12, 3pm (Zone)" when more than a day away — minutes, no seconds.
|
|
264
|
+
*/
|
|
265
|
+
export declare function formatResetLikeCli(atMs: number, timeZone?: string, nowMs?: number): string;
|
|
266
|
+
/**
|
|
267
|
+
* The daemon's startup banner line — pulled out of runAgentDaemon() so the printed
|
|
268
|
+
* version string is directly unit-testable without starting the real (infinite) loop.
|
|
269
|
+
*/
|
|
270
|
+
export declare function startupBanner(cfg: AgentConfig): string;
|
|
77
271
|
/** Run the daemon loop until aborted. */
|
|
78
272
|
export declare function runAgentDaemon(cfg: AgentConfig, signal?: AbortSignal): Promise<void>;
|
|
79
273
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"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"}
|