maestro-agent-sdk 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 +21 -0
- package/NOTICE +24 -0
- package/README.md +133 -0
- package/dist/agents/contracts.d.ts +49 -0
- package/dist/agents/contracts.d.ts.map +1 -0
- package/dist/agents/contracts.js +2 -0
- package/dist/agents/contracts.js.map +1 -0
- package/dist/agents/rollout/shared.d.ts +24 -0
- package/dist/agents/rollout/shared.d.ts.map +1 -0
- package/dist/agents/rollout/shared.js +105 -0
- package/dist/agents/rollout/shared.js.map +1 -0
- package/dist/core/agent.d.ts +71 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +22 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/loop.d.ts +26 -0
- package/dist/core/loop.d.ts.map +1 -0
- package/dist/core/loop.js +317 -0
- package/dist/core/loop.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +79 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +176 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/pool-cache.d.ts +103 -0
- package/dist/mcp/pool-cache.d.ts.map +1 -0
- package/dist/mcp/pool-cache.js +249 -0
- package/dist/mcp/pool-cache.js.map +1 -0
- package/dist/mcp/pool.d.ts +65 -0
- package/dist/mcp/pool.d.ts.map +1 -0
- package/dist/mcp/pool.js +86 -0
- package/dist/mcp/pool.js.map +1 -0
- package/dist/media/file-events.d.ts +8 -0
- package/dist/media/file-events.d.ts.map +1 -0
- package/dist/media/file-events.js +15 -0
- package/dist/media/file-events.js.map +1 -0
- package/dist/memory/active-task-template.d.ts +34 -0
- package/dist/memory/active-task-template.d.ts.map +1 -0
- package/dist/memory/active-task-template.js +63 -0
- package/dist/memory/active-task-template.js.map +1 -0
- package/dist/memory/compressor.d.ts +87 -0
- package/dist/memory/compressor.d.ts.map +1 -0
- package/dist/memory/compressor.js +164 -0
- package/dist/memory/compressor.js.map +1 -0
- package/dist/memory/hash.d.ts +17 -0
- package/dist/memory/hash.d.ts.map +1 -0
- package/dist/memory/hash.js +20 -0
- package/dist/memory/hash.js.map +1 -0
- package/dist/memory/prune.d.ts +117 -0
- package/dist/memory/prune.d.ts.map +1 -0
- package/dist/memory/prune.js +416 -0
- package/dist/memory/prune.js.map +1 -0
- package/dist/memory/reminder.d.ts +57 -0
- package/dist/memory/reminder.d.ts.map +1 -0
- package/dist/memory/reminder.js +57 -0
- package/dist/memory/reminder.js.map +1 -0
- package/dist/memory/scrubber.d.ts +28 -0
- package/dist/memory/scrubber.d.ts.map +1 -0
- package/dist/memory/scrubber.js +147 -0
- package/dist/memory/scrubber.js.map +1 -0
- package/dist/memory/token-estimate.d.ts +10 -0
- package/dist/memory/token-estimate.d.ts.map +1 -0
- package/dist/memory/token-estimate.js +69 -0
- package/dist/memory/token-estimate.js.map +1 -0
- package/dist/platform/config.d.ts +12 -0
- package/dist/platform/config.d.ts.map +1 -0
- package/dist/platform/config.js +54 -0
- package/dist/platform/config.js.map +1 -0
- package/dist/platform/jsonl.d.ts +15 -0
- package/dist/platform/jsonl.d.ts.map +1 -0
- package/dist/platform/jsonl.js +80 -0
- package/dist/platform/jsonl.js.map +1 -0
- package/dist/platform/lifecycle.d.ts +22 -0
- package/dist/platform/lifecycle.d.ts.map +1 -0
- package/dist/platform/lifecycle.js +60 -0
- package/dist/platform/lifecycle.js.map +1 -0
- package/dist/platform/logger.d.ts +26 -0
- package/dist/platform/logger.d.ts.map +1 -0
- package/dist/platform/logger.js +41 -0
- package/dist/platform/logger.js.map +1 -0
- package/dist/platform/mcp-config.d.ts +15 -0
- package/dist/platform/mcp-config.d.ts.map +1 -0
- package/dist/platform/mcp-config.js +8 -0
- package/dist/platform/mcp-config.js.map +1 -0
- package/dist/provider.d.ts +81 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +444 -0
- package/dist/provider.js.map +1 -0
- package/dist/providers/anthropic.d.ts +132 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +518 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +140 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +2 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/deepseek.d.ts +118 -0
- package/dist/providers/deepseek.d.ts.map +1 -0
- package/dist/providers/deepseek.js +467 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/registry.d.ts +3 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +94 -0
- package/dist/registry.js.map +1 -0
- package/dist/session-store.d.ts +133 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +277 -0
- package/dist/session-store.js.map +1 -0
- package/dist/skills/curator.d.ts +104 -0
- package/dist/skills/curator.d.ts.map +1 -0
- package/dist/skills/curator.js +162 -0
- package/dist/skills/curator.js.map +1 -0
- package/dist/skills/index-builder.d.ts +42 -0
- package/dist/skills/index-builder.d.ts.map +1 -0
- package/dist/skills/index-builder.js +94 -0
- package/dist/skills/index-builder.js.map +1 -0
- package/dist/skills/loader.d.ts +107 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +286 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/preprocess.d.ts +45 -0
- package/dist/skills/preprocess.d.ts.map +1 -0
- package/dist/skills/preprocess.js +126 -0
- package/dist/skills/preprocess.js.map +1 -0
- package/dist/skills/usage.d.ts +75 -0
- package/dist/skills/usage.d.ts.map +1 -0
- package/dist/skills/usage.js +147 -0
- package/dist/skills/usage.js.map +1 -0
- package/dist/state/todos.d.ts +95 -0
- package/dist/state/todos.d.ts.map +1 -0
- package/dist/state/todos.js +198 -0
- package/dist/state/todos.js.map +1 -0
- package/dist/storage/conversations.d.ts +28 -0
- package/dist/storage/conversations.d.ts.map +1 -0
- package/dist/storage/conversations.js +8 -0
- package/dist/storage/conversations.js.map +1 -0
- package/dist/sub-agent/runner.d.ts +78 -0
- package/dist/sub-agent/runner.d.ts.map +1 -0
- package/dist/sub-agent/runner.js +215 -0
- package/dist/sub-agent/runner.js.map +1 -0
- package/dist/tools/builtin/agent.d.ts +33 -0
- package/dist/tools/builtin/agent.d.ts.map +1 -0
- package/dist/tools/builtin/agent.js +76 -0
- package/dist/tools/builtin/agent.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +11 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -0
- package/dist/tools/builtin/bash.js +91 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/edit.d.ts +21 -0
- package/dist/tools/builtin/edit.d.ts.map +1 -0
- package/dist/tools/builtin/edit.js +238 -0
- package/dist/tools/builtin/edit.js.map +1 -0
- package/dist/tools/builtin/read.d.ts +17 -0
- package/dist/tools/builtin/read.d.ts.map +1 -0
- package/dist/tools/builtin/read.js +139 -0
- package/dist/tools/builtin/read.js.map +1 -0
- package/dist/tools/builtin/sandbox.d.ts +16 -0
- package/dist/tools/builtin/sandbox.d.ts.map +1 -0
- package/dist/tools/builtin/sandbox.js +58 -0
- package/dist/tools/builtin/sandbox.js.map +1 -0
- package/dist/tools/builtin/skill_view.d.ts +37 -0
- package/dist/tools/builtin/skill_view.d.ts.map +1 -0
- package/dist/tools/builtin/skill_view.js +82 -0
- package/dist/tools/builtin/skill_view.js.map +1 -0
- package/dist/tools/builtin/todo_write.d.ts +29 -0
- package/dist/tools/builtin/todo_write.d.ts.map +1 -0
- package/dist/tools/builtin/todo_write.js +96 -0
- package/dist/tools/builtin/todo_write.js.map +1 -0
- package/dist/tools/builtin/web_fetch.d.ts +10 -0
- package/dist/tools/builtin/web_fetch.d.ts.map +1 -0
- package/dist/tools/builtin/web_fetch.js +150 -0
- package/dist/tools/builtin/web_fetch.js.map +1 -0
- package/dist/tools/builtin/write.d.ts +35 -0
- package/dist/tools/builtin/write.d.ts.map +1 -0
- package/dist/tools/builtin/write.js +70 -0
- package/dist/tools/builtin/write.js.map +1 -0
- package/dist/tools/file-state.d.ts +99 -0
- package/dist/tools/file-state.d.ts.map +1 -0
- package/dist/tools/file-state.js +133 -0
- package/dist/tools/file-state.js.map +1 -0
- package/dist/tools/hooks/sandbox-fs.d.ts +25 -0
- package/dist/tools/hooks/sandbox-fs.d.ts.map +1 -0
- package/dist/tools/hooks/sandbox-fs.js +48 -0
- package/dist/tools/hooks/sandbox-fs.js.map +1 -0
- package/dist/tools/registry.d.ts +102 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +93 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
/**
|
|
3
|
+
* One tracker per session — `Map<absPath, FileStateEntry>`.
|
|
4
|
+
*
|
|
5
|
+
* Wrapped (instead of a raw Map) so the public surface is explicit: callers
|
|
6
|
+
* only record and check, they don't iterate or mutate entries directly.
|
|
7
|
+
*/
|
|
8
|
+
export class FileStateTracker {
|
|
9
|
+
entries = new Map();
|
|
10
|
+
/**
|
|
11
|
+
* Record a successful Read of `absPath`. Called from Read's execute()
|
|
12
|
+
* after the file has been stat'd and the contents handed back to the
|
|
13
|
+
* model. A subsequent Read of the same path overwrites the entry — the
|
|
14
|
+
* latest Read is what Edit's drift check compares against.
|
|
15
|
+
*/
|
|
16
|
+
recordRead(absPath, mtime, size) {
|
|
17
|
+
this.entries.set(absPath, { mtime, size, readAt: Date.now() });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Forget the recorded state for `absPath`. Called when Edit / Write
|
|
21
|
+
* mutates the file so the next Edit must Re-read first (its prior cached
|
|
22
|
+
* stat is now stale by definition). Also called when a Write creates a
|
|
23
|
+
* file — the next Edit on the same path must Read it back to confirm
|
|
24
|
+
* the model's mental model matches what landed on disk.
|
|
25
|
+
*/
|
|
26
|
+
forget(absPath) {
|
|
27
|
+
this.entries.delete(absPath);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check whether `absPath` may be Edit/Write'd. Returns null on allow,
|
|
31
|
+
* or a string error on deny — callers stringify into `{error}` so the
|
|
32
|
+
* model sees the same shape as sandbox / arg-validation rejections.
|
|
33
|
+
*
|
|
34
|
+
* Three reject paths:
|
|
35
|
+
* - "no_read": never Read this session. Forces the model to Read first.
|
|
36
|
+
* - "stale_mtime": mtime drifted vs recorded — file changed externally.
|
|
37
|
+
* - "stale_size": same as above but size changed (covers same-ms writes).
|
|
38
|
+
*
|
|
39
|
+
* Allow path: live stat matches recorded mtime AND size.
|
|
40
|
+
*
|
|
41
|
+
* One subtlety: if the path doesn't exist, we return null (allow). Write
|
|
42
|
+
* legitimately creates files, and Edit's "must exist" check runs after
|
|
43
|
+
* this gate inside the tool — so a missing file in Edit will reject with
|
|
44
|
+
* "file does not exist, use Write to create". Read-before-Mutate is about
|
|
45
|
+
* content drift, not existence.
|
|
46
|
+
*/
|
|
47
|
+
checkBeforeMutate(absPath, tool) {
|
|
48
|
+
if (!existsSync(absPath))
|
|
49
|
+
return null;
|
|
50
|
+
// Write on an existing file requires prior Read — same invariant as Edit.
|
|
51
|
+
// The model must see the current line-numbered content before overwriting
|
|
52
|
+
// to prevent blind overwrites and stale-content races.
|
|
53
|
+
// Write on a non-existent path (creation) is always allowed; the gate
|
|
54
|
+
// short-circuits at the `existsSync` check above.
|
|
55
|
+
const entry = this.entries.get(absPath);
|
|
56
|
+
if (!entry) {
|
|
57
|
+
return (`${tool}: file '${absPath}' has not been Read in this session. ` +
|
|
58
|
+
`Read it first so the model sees the current line-numbered content, ` +
|
|
59
|
+
`then re-issue the ${tool}.`);
|
|
60
|
+
}
|
|
61
|
+
let live;
|
|
62
|
+
try {
|
|
63
|
+
const s = statSync(absPath);
|
|
64
|
+
live = { mtimeMs: s.mtimeMs, size: s.size };
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// If we can't stat, fall through to the tool's own error handler — it
|
|
68
|
+
// will surface a clearer message than we can.
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (live.mtimeMs !== entry.mtime) {
|
|
72
|
+
const ageMs = Date.now() - entry.readAt;
|
|
73
|
+
return (`${tool}: file '${absPath}' was modified externally since the last Read ` +
|
|
74
|
+
`(recorded mtime ${entry.mtime}, live mtime ${live.mtimeMs}, ` +
|
|
75
|
+
`Read was ${Math.round(ageMs / 1000)}s ago). ` +
|
|
76
|
+
`Re-Read the file to refresh the line-numbered view, then re-issue the ${tool}.`);
|
|
77
|
+
}
|
|
78
|
+
if (live.size !== entry.size) {
|
|
79
|
+
return (`${tool}: file '${absPath}' size changed since the last Read ` +
|
|
80
|
+
`(recorded ${entry.size}, live ${live.size}). ` +
|
|
81
|
+
`Re-Read the file before ${tool.toLowerCase()}ing.`);
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/** Used by tests + diagnostics. Not part of the per-turn contract. */
|
|
86
|
+
has(absPath) {
|
|
87
|
+
return this.entries.has(absPath);
|
|
88
|
+
}
|
|
89
|
+
/** Total number of tracked paths. Diagnostic. */
|
|
90
|
+
size() {
|
|
91
|
+
return this.entries.size;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Module-level registry: `sessionId → FileStateTracker`.
|
|
96
|
+
*
|
|
97
|
+
* Why module scope (not registry-scope or agent-scope): `maestroProvider`
|
|
98
|
+
* builds a fresh `ToolRegistry` per turn but the sessionId persists across
|
|
99
|
+
* turns. A registry-local tracker would reset every turn and the Read-before-
|
|
100
|
+
* Edit gate would become useless because the Read happened "last turn" and
|
|
101
|
+
* the tracker forgot.
|
|
102
|
+
*
|
|
103
|
+
* Cleanup is wired in two places — `deleteMaestroSession` (explicit) and
|
|
104
|
+
* `cleanupStaleMaestroSessions` (per-file unlink loop) — so the map can't
|
|
105
|
+
* outlive its sessions.
|
|
106
|
+
*/
|
|
107
|
+
const sessions = new Map();
|
|
108
|
+
/** Get or create the tracker for a session. */
|
|
109
|
+
export function getFileStateTracker(sessionId) {
|
|
110
|
+
let t = sessions.get(sessionId);
|
|
111
|
+
if (!t) {
|
|
112
|
+
t = new FileStateTracker();
|
|
113
|
+
sessions.set(sessionId, t);
|
|
114
|
+
}
|
|
115
|
+
return t;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Drop a session's tracker. Called from `deleteMaestroSession` and from
|
|
119
|
+
* `cleanupStaleMaestroSessions` after the on-disk JSONL is unlinked.
|
|
120
|
+
* ENOENT-equivalent: deleting a session that was never tracked is a no-op.
|
|
121
|
+
*/
|
|
122
|
+
export function dropFileStateTracker(sessionId) {
|
|
123
|
+
sessions.delete(sessionId);
|
|
124
|
+
}
|
|
125
|
+
/** Test-only. Drops every tracker. */
|
|
126
|
+
export function __resetAllTrackers() {
|
|
127
|
+
sessions.clear();
|
|
128
|
+
}
|
|
129
|
+
/** Test-only. Snapshot of how many sessions are currently tracked. */
|
|
130
|
+
export function __trackerCount() {
|
|
131
|
+
return sessions.size;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=file-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-state.js","sourceRoot":"","sources":["../../src/tools/file-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { HookRegistration } from "../../tools/registry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Filesystem sandbox as a PreToolUse hook.
|
|
4
|
+
*
|
|
5
|
+
* Centralized opt-in gate for any tool whose input carries a `file_path`
|
|
6
|
+
* argument (Read/Write/Edit today, future FS-touching MCP tools by
|
|
7
|
+
* inheritance). Default is **disabled** to match the unconstrained FS
|
|
8
|
+
* posture of claude (`bypassPermissions`) and codex (`danger-full-access`)
|
|
9
|
+
* providers — divergence here used to silently break maestro-only workflows
|
|
10
|
+
* that legitimately reach outside the workspace. Operator opts in by
|
|
11
|
+
* exporting `MAESTRO_FS_SANDBOX_ENABLED=1`.
|
|
12
|
+
*
|
|
13
|
+
* Scope: only tools whose `input.file_path` is a non-empty string are
|
|
14
|
+
* inspected. Tools that don't surface a file path (bash, web_fetch, MCP
|
|
15
|
+
* tools without an FS argument) are unaffected — this hook is FS-specific
|
|
16
|
+
* by design. A future bash-sandbox hook would be a separate registration.
|
|
17
|
+
*
|
|
18
|
+
* Absolute-path enforcement is left to the tool itself: the model's error
|
|
19
|
+
* message is clearer when it comes from the tool (`Read: file_path must be
|
|
20
|
+
* absolute`) than from the sandbox hook ("path is not absolute"). The
|
|
21
|
+
* sandbox only weighs in once an absolute path has been confirmed —
|
|
22
|
+
* mirroring how the inline check used to work.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createSandboxFsHook(): HookRegistration;
|
|
25
|
+
//# sourceMappingURL=sandbox-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-fs.d.ts","sourceRoot":"","sources":["../../../src/tools/hooks/sandbox-fs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CAsBtD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isAbsolute } from "node:path";
|
|
2
|
+
import { checkFilesystemAccess, isSandboxEnabled } from "../../tools/builtin/sandbox.js";
|
|
3
|
+
/**
|
|
4
|
+
* Filesystem sandbox as a PreToolUse hook.
|
|
5
|
+
*
|
|
6
|
+
* Centralized opt-in gate for any tool whose input carries a `file_path`
|
|
7
|
+
* argument (Read/Write/Edit today, future FS-touching MCP tools by
|
|
8
|
+
* inheritance). Default is **disabled** to match the unconstrained FS
|
|
9
|
+
* posture of claude (`bypassPermissions`) and codex (`danger-full-access`)
|
|
10
|
+
* providers — divergence here used to silently break maestro-only workflows
|
|
11
|
+
* that legitimately reach outside the workspace. Operator opts in by
|
|
12
|
+
* exporting `MAESTRO_FS_SANDBOX_ENABLED=1`.
|
|
13
|
+
*
|
|
14
|
+
* Scope: only tools whose `input.file_path` is a non-empty string are
|
|
15
|
+
* inspected. Tools that don't surface a file path (bash, web_fetch, MCP
|
|
16
|
+
* tools without an FS argument) are unaffected — this hook is FS-specific
|
|
17
|
+
* by design. A future bash-sandbox hook would be a separate registration.
|
|
18
|
+
*
|
|
19
|
+
* Absolute-path enforcement is left to the tool itself: the model's error
|
|
20
|
+
* message is clearer when it comes from the tool (`Read: file_path must be
|
|
21
|
+
* absolute`) than from the sandbox hook ("path is not absolute"). The
|
|
22
|
+
* sandbox only weighs in once an absolute path has been confirmed —
|
|
23
|
+
* mirroring how the inline check used to work.
|
|
24
|
+
*/
|
|
25
|
+
export function createSandboxFsHook() {
|
|
26
|
+
return {
|
|
27
|
+
name: "sandbox-fs",
|
|
28
|
+
pre(ctx) {
|
|
29
|
+
// Cheap exit when the operator hasn't opted into the sandbox — the
|
|
30
|
+
// common case, so skip the work before doing any property reads.
|
|
31
|
+
if (!isSandboxEnabled())
|
|
32
|
+
return { decision: "allow" };
|
|
33
|
+
const filePath = ctx.input.file_path;
|
|
34
|
+
if (typeof filePath !== "string" || filePath.length === 0) {
|
|
35
|
+
return { decision: "allow" };
|
|
36
|
+
}
|
|
37
|
+
// Defer absolute-path errors to the tool — its message is clearer.
|
|
38
|
+
if (!isAbsolute(filePath))
|
|
39
|
+
return { decision: "allow" };
|
|
40
|
+
const err = checkFilesystemAccess(filePath);
|
|
41
|
+
if (err) {
|
|
42
|
+
return { decision: "block", error: `${ctx.toolName}: ${err}` };
|
|
43
|
+
}
|
|
44
|
+
return { decision: "allow" };
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=sandbox-fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-fs.js","sourceRoot":"","sources":["../../../src/tools/hooks/sandbox-fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGlF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,GAAG,CAAC,GAAG;YACL,mEAAmE;YACnE,iEAAiE;YACjE,IAAI,CAAC,gBAAgB,EAAE;gBAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YAEtD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;YACrC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YAC/B,CAAC;YACD,mEAAmE;YACnE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YAExD,MAAM,GAAG,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,QAAQ,KAAK,GAAG,EAAE,EAAE,CAAC;YACjE,CAAC;YACD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QAC/B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ProviderToolSchema } from "../providers/base.js";
|
|
2
|
+
/**
|
|
3
|
+
* Maestro tool registry — TS port of upstream `tools/registry.py`.
|
|
4
|
+
*
|
|
5
|
+
* Holds a flat map of tool name → handler. Schemas are surfaced to the
|
|
6
|
+
* provider via `schemas()` so the model knows what's callable; dispatch
|
|
7
|
+
* routes a `tool_use` block back to the registered handler.
|
|
8
|
+
*
|
|
9
|
+
* Phase 2 adds a hook chain around `dispatch` (PreToolUse / PostToolUse)
|
|
10
|
+
* matching Claude Code's settings.json hook surface. Hooks centralize
|
|
11
|
+
* policy that would otherwise be sprinkled across tools — sandbox gates,
|
|
12
|
+
* automatic redaction, telemetry.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Decision returned by a PreToolUse hook.
|
|
16
|
+
* - `allow` → continue to the next hook (or execute() if last).
|
|
17
|
+
* - `modify` → continue with the supplied `input` substituted.
|
|
18
|
+
* - `block` → short-circuit chain. `error` wraps to `{error}`; PostToolUse
|
|
19
|
+
* hooks do NOT run on blocked calls.
|
|
20
|
+
*/
|
|
21
|
+
export type PreToolUseDecision = {
|
|
22
|
+
decision: "allow";
|
|
23
|
+
} | {
|
|
24
|
+
decision: "modify";
|
|
25
|
+
input: Record<string, unknown>;
|
|
26
|
+
} | {
|
|
27
|
+
decision: "block";
|
|
28
|
+
error: string;
|
|
29
|
+
};
|
|
30
|
+
export interface PreToolUseContext {
|
|
31
|
+
toolName: string;
|
|
32
|
+
input: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
export interface PostToolUseContext {
|
|
35
|
+
toolName: string;
|
|
36
|
+
/** The input the tool actually saw — already mutated if a Pre hook modified. */
|
|
37
|
+
input: Record<string, unknown>;
|
|
38
|
+
/** The string returned by `execute()` (or by an upstream Post hook). */
|
|
39
|
+
output: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* PostToolUse result. All optional:
|
|
43
|
+
* - `output` → replace the surfaced result (redaction, truncation).
|
|
44
|
+
* - `log` → fire-and-forget structured payload for telemetry sinks.
|
|
45
|
+
*/
|
|
46
|
+
export interface PostToolUseResult {
|
|
47
|
+
output?: string;
|
|
48
|
+
log?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
export type PreToolUseHook = (ctx: PreToolUseContext) => Promise<PreToolUseDecision> | PreToolUseDecision;
|
|
51
|
+
export type PostToolUseHook = (ctx: PostToolUseContext) => Promise<PostToolUseResult> | PostToolUseResult;
|
|
52
|
+
export interface HookRegistration {
|
|
53
|
+
name?: string;
|
|
54
|
+
pre?: PreToolUseHook;
|
|
55
|
+
post?: PostToolUseHook;
|
|
56
|
+
}
|
|
57
|
+
export interface ToolHandler {
|
|
58
|
+
schema: ProviderToolSchema;
|
|
59
|
+
/**
|
|
60
|
+
* Whether this tool is safe to dispatch in parallel with other parallel-
|
|
61
|
+
* safe tools in the same turn. Anthropic can return multiple `tool_use`
|
|
62
|
+
* blocks in one assistant response; running independent reads in parallel
|
|
63
|
+
* cuts latency dramatically. Default false (safer). Mark true on tools
|
|
64
|
+
* with no observable side effects (pure reads, idempotent fetches).
|
|
65
|
+
*/
|
|
66
|
+
parallelSafe?: boolean;
|
|
67
|
+
execute(input: Record<string, unknown>): Promise<string>;
|
|
68
|
+
}
|
|
69
|
+
export declare class ToolRegistry {
|
|
70
|
+
private readonly tools;
|
|
71
|
+
private readonly hooks;
|
|
72
|
+
register(handler: ToolHandler): void;
|
|
73
|
+
/**
|
|
74
|
+
* Register a hook pair (pre and/or post). Hooks fire in registration
|
|
75
|
+
* order — pre forward, post forward. "Last in, last to react" beats a
|
|
76
|
+
* Koa-style onion model here because each hook is independent; a
|
|
77
|
+
* telemetry hook generally wants to log AFTER a redaction hook has
|
|
78
|
+
* scrubbed the output, which matches registration intent.
|
|
79
|
+
*/
|
|
80
|
+
use(reg: HookRegistration): void;
|
|
81
|
+
/** Diagnostic: count of registered hooks. */
|
|
82
|
+
__hookCount(): number;
|
|
83
|
+
has(name: string): boolean;
|
|
84
|
+
schemas(): ProviderToolSchema[];
|
|
85
|
+
/**
|
|
86
|
+
* Dispatch through the full hook chain:
|
|
87
|
+
* 1. Pre hooks in order. First `block` wins → wrapped as {error}; skip
|
|
88
|
+
* execute() and skip every Post hook (no clean result to process).
|
|
89
|
+
* 2. `execute()`. Thrown errors → {error}; Post hooks skipped.
|
|
90
|
+
* 3. Post hooks in order; each may mutate output and/or emit a log.
|
|
91
|
+
* A Post hook that throws is swallowed so it can't poison the result.
|
|
92
|
+
*/
|
|
93
|
+
dispatch(name: string, input: Record<string, unknown>): Promise<string>;
|
|
94
|
+
/**
|
|
95
|
+
* True when `name` is registered AND its handler is flagged parallelSafe.
|
|
96
|
+
* Unknown tools and unflagged tools default to false — the loop falls
|
|
97
|
+
* through to sequential dispatch, which is the safe default for any tool
|
|
98
|
+
* with side effects (write/edit/bash/most MCP).
|
|
99
|
+
*/
|
|
100
|
+
isParallelSafe(name: string): boolean;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D;;;;;;;;;;;GAWG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,GACrB;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACtD;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,GAAG,EAAE,iBAAiB,KACnB,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,CAAC;AAEtD,MAAM,MAAM,eAAe,GAAG,CAC5B,GAAG,EAAE,kBAAkB,KACpB,OAAO,CAAC,iBAAiB,CAAC,GAAG,iBAAiB,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,kBAAkB,CAAC;IAC3B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1D;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAEhD,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAQpC;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IAIhC,6CAA6C;IAC7C,WAAW,IAAI,MAAM;IAIrB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,OAAO,IAAI,kBAAkB,EAAE;IAI/B;;;;;;;OAOG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IA6C7E;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAGtC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export class ToolRegistry {
|
|
2
|
+
tools = new Map();
|
|
3
|
+
hooks = [];
|
|
4
|
+
register(handler) {
|
|
5
|
+
const name = handler.schema.name;
|
|
6
|
+
if (this.tools.has(name)) {
|
|
7
|
+
throw new Error(`Maestro ToolRegistry: tool '${name}' is already registered`);
|
|
8
|
+
}
|
|
9
|
+
this.tools.set(name, handler);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Register a hook pair (pre and/or post). Hooks fire in registration
|
|
13
|
+
* order — pre forward, post forward. "Last in, last to react" beats a
|
|
14
|
+
* Koa-style onion model here because each hook is independent; a
|
|
15
|
+
* telemetry hook generally wants to log AFTER a redaction hook has
|
|
16
|
+
* scrubbed the output, which matches registration intent.
|
|
17
|
+
*/
|
|
18
|
+
use(reg) {
|
|
19
|
+
this.hooks.push(reg);
|
|
20
|
+
}
|
|
21
|
+
/** Diagnostic: count of registered hooks. */
|
|
22
|
+
__hookCount() {
|
|
23
|
+
return this.hooks.length;
|
|
24
|
+
}
|
|
25
|
+
has(name) {
|
|
26
|
+
return this.tools.has(name);
|
|
27
|
+
}
|
|
28
|
+
schemas() {
|
|
29
|
+
return Array.from(this.tools.values()).map((h) => h.schema);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Dispatch through the full hook chain:
|
|
33
|
+
* 1. Pre hooks in order. First `block` wins → wrapped as {error}; skip
|
|
34
|
+
* execute() and skip every Post hook (no clean result to process).
|
|
35
|
+
* 2. `execute()`. Thrown errors → {error}; Post hooks skipped.
|
|
36
|
+
* 3. Post hooks in order; each may mutate output and/or emit a log.
|
|
37
|
+
* A Post hook that throws is swallowed so it can't poison the result.
|
|
38
|
+
*/
|
|
39
|
+
async dispatch(name, input) {
|
|
40
|
+
const handler = this.tools.get(name);
|
|
41
|
+
if (!handler) {
|
|
42
|
+
return JSON.stringify({ error: `unknown tool: ${name}` });
|
|
43
|
+
}
|
|
44
|
+
let currentInput = input;
|
|
45
|
+
for (const hook of this.hooks) {
|
|
46
|
+
if (!hook.pre)
|
|
47
|
+
continue;
|
|
48
|
+
const decision = await hook.pre({ toolName: name, input: currentInput });
|
|
49
|
+
if (decision.decision === "block") {
|
|
50
|
+
return JSON.stringify({ error: decision.error });
|
|
51
|
+
}
|
|
52
|
+
if (decision.decision === "modify") {
|
|
53
|
+
currentInput = decision.input;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let output;
|
|
57
|
+
try {
|
|
58
|
+
output = await handler.execute(currentInput);
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
62
|
+
return JSON.stringify({ error: msg });
|
|
63
|
+
}
|
|
64
|
+
for (const hook of this.hooks) {
|
|
65
|
+
if (!hook.post)
|
|
66
|
+
continue;
|
|
67
|
+
try {
|
|
68
|
+
const result = await hook.post({ toolName: name, input: currentInput, output });
|
|
69
|
+
if (typeof result.output === "string") {
|
|
70
|
+
output = result.output;
|
|
71
|
+
}
|
|
72
|
+
// `log` is fire-and-forget. Registry has no opinion on sink; the
|
|
73
|
+
// hook owns wherever it routes the payload.
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
// Post-hook crash must not corrupt the user-visible result.
|
|
77
|
+
// Telemetry hooks should be the ones capturing these errors.
|
|
78
|
+
void e;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return output;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* True when `name` is registered AND its handler is flagged parallelSafe.
|
|
85
|
+
* Unknown tools and unflagged tools default to false — the loop falls
|
|
86
|
+
* through to sequential dispatch, which is the safe default for any tool
|
|
87
|
+
* with side effects (write/edit/bash/most MCP).
|
|
88
|
+
*/
|
|
89
|
+
isParallelSafe(name) {
|
|
90
|
+
return this.tools.get(name)?.parallelSafe === true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core SDK types — narrowed from the upstream clawgram types.
|
|
3
|
+
*
|
|
4
|
+
* The host application provides the rest (PII context, session bookkeeping,
|
|
5
|
+
* Telegram-specific fields). The SDK only needs the pieces the agent loop,
|
|
6
|
+
* providers, and tools actually consume.
|
|
7
|
+
*/
|
|
8
|
+
export interface TokenUsage {
|
|
9
|
+
inputTokens: number;
|
|
10
|
+
outputTokens: number;
|
|
11
|
+
cacheCreationInputTokens?: number;
|
|
12
|
+
cacheReadInputTokens?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Effort levels accepted by Maestro providers.
|
|
16
|
+
*
|
|
17
|
+
* Anthropic ignores this and uses `thinkingBudget`; DeepSeek maps it to
|
|
18
|
+
* `reasoning_effort` directly. Hosts can pass either string form.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MAESTRO_EFFORT_VALUES: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
21
|
+
export type EffortLevel = "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
22
|
+
/**
|
|
23
|
+
* Identifier returned by the SDK's session-store. Hosts may map their own
|
|
24
|
+
* notion of "session" (e.g. a topic, a thread, a user) onto this.
|
|
25
|
+
*/
|
|
26
|
+
export type AgentKind = "claude" | "codex" | "maestro";
|
|
27
|
+
export declare const SUPPORTED_AGENTS: readonly AgentKind[];
|
|
28
|
+
export declare const FALLBACK_AGENT: AgentKind;
|
|
29
|
+
export declare function isAgentKind(value: unknown): value is AgentKind;
|
|
30
|
+
/**
|
|
31
|
+
* Normalized event stream emitted by the agent loop. Hosts iterate this
|
|
32
|
+
* async generator and render / persist whichever variants they care about.
|
|
33
|
+
*/
|
|
34
|
+
export type UnifiedEvent = {
|
|
35
|
+
type: "user_message";
|
|
36
|
+
content: string;
|
|
37
|
+
} | {
|
|
38
|
+
type: "session";
|
|
39
|
+
sessionId: string;
|
|
40
|
+
} | {
|
|
41
|
+
type: "tool_use";
|
|
42
|
+
name: string;
|
|
43
|
+
input: Record<string, unknown>;
|
|
44
|
+
} | {
|
|
45
|
+
type: "tool_progress";
|
|
46
|
+
toolName: string;
|
|
47
|
+
elapsed: number;
|
|
48
|
+
} | {
|
|
49
|
+
type: "tool_use_summary";
|
|
50
|
+
summary: string;
|
|
51
|
+
} | {
|
|
52
|
+
type: "tool_result";
|
|
53
|
+
toolUseId: string;
|
|
54
|
+
content: string;
|
|
55
|
+
} | {
|
|
56
|
+
type: "text_delta";
|
|
57
|
+
content: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: "text";
|
|
60
|
+
content: string;
|
|
61
|
+
} | {
|
|
62
|
+
type: "result";
|
|
63
|
+
content: string;
|
|
64
|
+
stopReason: string;
|
|
65
|
+
usage?: TokenUsage;
|
|
66
|
+
} | {
|
|
67
|
+
type: "file";
|
|
68
|
+
path: string;
|
|
69
|
+
source: string;
|
|
70
|
+
origin: "tag" | "extension";
|
|
71
|
+
} | {
|
|
72
|
+
type: "error";
|
|
73
|
+
content: string;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Options accepted by `maestroProvider.query()` — the host-facing entry point.
|
|
77
|
+
*
|
|
78
|
+
* Most fields are optional; only `prompt`, `cwd`, `systemPrompt` are strictly
|
|
79
|
+
* required to drive the loop. The rest let the host plug in session
|
|
80
|
+
* persistence, sub-agent delegation, MCP servers, and abort control.
|
|
81
|
+
*/
|
|
82
|
+
export interface AgentQueryOptions {
|
|
83
|
+
agent: AgentKind;
|
|
84
|
+
prompt: string;
|
|
85
|
+
sessionId?: string | null;
|
|
86
|
+
cwd: string;
|
|
87
|
+
systemPrompt: string;
|
|
88
|
+
userId?: string;
|
|
89
|
+
session?: string;
|
|
90
|
+
sessionType?: "dm" | "forum" | "ephemeral" | "manager";
|
|
91
|
+
groupId?: number;
|
|
92
|
+
abortController?: AbortController;
|
|
93
|
+
model?: string;
|
|
94
|
+
depth?: number;
|
|
95
|
+
agents?: Record<string, {
|
|
96
|
+
description: string;
|
|
97
|
+
prompt: string;
|
|
98
|
+
model?: string;
|
|
99
|
+
tools?: string[];
|
|
100
|
+
maxTurns?: number;
|
|
101
|
+
effort?: EffortLevel | number;
|
|
102
|
+
}>;
|
|
103
|
+
effort?: EffortLevel;
|
|
104
|
+
mcpEnabled?: string[] | null;
|
|
105
|
+
mcpExtra?: Record<string, unknown>;
|
|
106
|
+
isCron?: boolean;
|
|
107
|
+
silent?: boolean;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core SDK types — narrowed from the upstream clawgram types.
|
|
3
|
+
*
|
|
4
|
+
* The host application provides the rest (PII context, session bookkeeping,
|
|
5
|
+
* Telegram-specific fields). The SDK only needs the pieces the agent loop,
|
|
6
|
+
* providers, and tools actually consume.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Effort levels accepted by Maestro providers.
|
|
10
|
+
*
|
|
11
|
+
* Anthropic ignores this and uses `thinkingBudget`; DeepSeek maps it to
|
|
12
|
+
* `reasoning_effort` directly. Hosts can pass either string form.
|
|
13
|
+
*/
|
|
14
|
+
export const MAESTRO_EFFORT_VALUES = ["low", "medium", "high", "xhigh", "max"];
|
|
15
|
+
export const SUPPORTED_AGENTS = ["claude", "codex", "maestro"];
|
|
16
|
+
export const FALLBACK_AGENT = "claude";
|
|
17
|
+
export function isAgentKind(value) {
|
|
18
|
+
return typeof value === "string" && SUPPORTED_AGENTS.includes(value);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAU,CAAC;AAQxF,MAAM,CAAC,MAAM,gBAAgB,GAAyB,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAU,CAAC;AAC9F,MAAM,CAAC,MAAM,cAAc,GAAc,QAAQ,CAAC;AAElD,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,gBAAsC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC9F,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "maestro-agent-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Embeddable, provider-agnostic TypeScript agent SDK — pluggable providers, built-in tools, skills, memory, and MCP.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Yeonwoo Jeong <aadd4020@gmail.com>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/maestrojeong/maestro-agent-sdk.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./providers/anthropic": {
|
|
20
|
+
"types": "./dist/providers/anthropic.d.ts",
|
|
21
|
+
"import": "./dist/providers/anthropic.js"
|
|
22
|
+
},
|
|
23
|
+
"./providers/deepseek": {
|
|
24
|
+
"types": "./dist/providers/deepseek.d.ts",
|
|
25
|
+
"import": "./dist/providers/deepseek.js"
|
|
26
|
+
},
|
|
27
|
+
"./tools": {
|
|
28
|
+
"types": "./dist/tools/index.d.ts",
|
|
29
|
+
"import": "./dist/tools/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE",
|
|
36
|
+
"NOTICE"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json --resolve-full-paths",
|
|
40
|
+
"clean": "rm -rf dist",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"agent",
|
|
48
|
+
"ai-agent",
|
|
49
|
+
"llm",
|
|
50
|
+
"anthropic",
|
|
51
|
+
"claude",
|
|
52
|
+
"deepseek",
|
|
53
|
+
"mcp",
|
|
54
|
+
"tool-use",
|
|
55
|
+
"agent-sdk",
|
|
56
|
+
"hermes-agent",
|
|
57
|
+
"maestro"
|
|
58
|
+
],
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=20"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
64
|
+
"cheerio": "^1.2.0"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@types/node": "^20",
|
|
68
|
+
"tsc-alias": "^1.8.17",
|
|
69
|
+
"typescript": "^5",
|
|
70
|
+
"vitest": "^4.1.6"
|
|
71
|
+
}
|
|
72
|
+
}
|