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.
Files changed (196) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +24 -0
  3. package/README.md +133 -0
  4. package/dist/agents/contracts.d.ts +49 -0
  5. package/dist/agents/contracts.d.ts.map +1 -0
  6. package/dist/agents/contracts.js +2 -0
  7. package/dist/agents/contracts.js.map +1 -0
  8. package/dist/agents/rollout/shared.d.ts +24 -0
  9. package/dist/agents/rollout/shared.d.ts.map +1 -0
  10. package/dist/agents/rollout/shared.js +105 -0
  11. package/dist/agents/rollout/shared.js.map +1 -0
  12. package/dist/core/agent.d.ts +71 -0
  13. package/dist/core/agent.d.ts.map +1 -0
  14. package/dist/core/agent.js +22 -0
  15. package/dist/core/agent.js.map +1 -0
  16. package/dist/core/loop.d.ts +26 -0
  17. package/dist/core/loop.d.ts.map +1 -0
  18. package/dist/core/loop.js +317 -0
  19. package/dist/core/loop.js.map +1 -0
  20. package/dist/index.d.ts +49 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +53 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/mcp/client.d.ts +79 -0
  25. package/dist/mcp/client.d.ts.map +1 -0
  26. package/dist/mcp/client.js +176 -0
  27. package/dist/mcp/client.js.map +1 -0
  28. package/dist/mcp/pool-cache.d.ts +103 -0
  29. package/dist/mcp/pool-cache.d.ts.map +1 -0
  30. package/dist/mcp/pool-cache.js +249 -0
  31. package/dist/mcp/pool-cache.js.map +1 -0
  32. package/dist/mcp/pool.d.ts +65 -0
  33. package/dist/mcp/pool.d.ts.map +1 -0
  34. package/dist/mcp/pool.js +86 -0
  35. package/dist/mcp/pool.js.map +1 -0
  36. package/dist/media/file-events.d.ts +8 -0
  37. package/dist/media/file-events.d.ts.map +1 -0
  38. package/dist/media/file-events.js +15 -0
  39. package/dist/media/file-events.js.map +1 -0
  40. package/dist/memory/active-task-template.d.ts +34 -0
  41. package/dist/memory/active-task-template.d.ts.map +1 -0
  42. package/dist/memory/active-task-template.js +63 -0
  43. package/dist/memory/active-task-template.js.map +1 -0
  44. package/dist/memory/compressor.d.ts +87 -0
  45. package/dist/memory/compressor.d.ts.map +1 -0
  46. package/dist/memory/compressor.js +164 -0
  47. package/dist/memory/compressor.js.map +1 -0
  48. package/dist/memory/hash.d.ts +17 -0
  49. package/dist/memory/hash.d.ts.map +1 -0
  50. package/dist/memory/hash.js +20 -0
  51. package/dist/memory/hash.js.map +1 -0
  52. package/dist/memory/prune.d.ts +117 -0
  53. package/dist/memory/prune.d.ts.map +1 -0
  54. package/dist/memory/prune.js +416 -0
  55. package/dist/memory/prune.js.map +1 -0
  56. package/dist/memory/reminder.d.ts +57 -0
  57. package/dist/memory/reminder.d.ts.map +1 -0
  58. package/dist/memory/reminder.js +57 -0
  59. package/dist/memory/reminder.js.map +1 -0
  60. package/dist/memory/scrubber.d.ts +28 -0
  61. package/dist/memory/scrubber.d.ts.map +1 -0
  62. package/dist/memory/scrubber.js +147 -0
  63. package/dist/memory/scrubber.js.map +1 -0
  64. package/dist/memory/token-estimate.d.ts +10 -0
  65. package/dist/memory/token-estimate.d.ts.map +1 -0
  66. package/dist/memory/token-estimate.js +69 -0
  67. package/dist/memory/token-estimate.js.map +1 -0
  68. package/dist/platform/config.d.ts +12 -0
  69. package/dist/platform/config.d.ts.map +1 -0
  70. package/dist/platform/config.js +54 -0
  71. package/dist/platform/config.js.map +1 -0
  72. package/dist/platform/jsonl.d.ts +15 -0
  73. package/dist/platform/jsonl.d.ts.map +1 -0
  74. package/dist/platform/jsonl.js +80 -0
  75. package/dist/platform/jsonl.js.map +1 -0
  76. package/dist/platform/lifecycle.d.ts +22 -0
  77. package/dist/platform/lifecycle.d.ts.map +1 -0
  78. package/dist/platform/lifecycle.js +60 -0
  79. package/dist/platform/lifecycle.js.map +1 -0
  80. package/dist/platform/logger.d.ts +26 -0
  81. package/dist/platform/logger.d.ts.map +1 -0
  82. package/dist/platform/logger.js +41 -0
  83. package/dist/platform/logger.js.map +1 -0
  84. package/dist/platform/mcp-config.d.ts +15 -0
  85. package/dist/platform/mcp-config.d.ts.map +1 -0
  86. package/dist/platform/mcp-config.js +8 -0
  87. package/dist/platform/mcp-config.js.map +1 -0
  88. package/dist/provider.d.ts +81 -0
  89. package/dist/provider.d.ts.map +1 -0
  90. package/dist/provider.js +444 -0
  91. package/dist/provider.js.map +1 -0
  92. package/dist/providers/anthropic.d.ts +132 -0
  93. package/dist/providers/anthropic.d.ts.map +1 -0
  94. package/dist/providers/anthropic.js +518 -0
  95. package/dist/providers/anthropic.js.map +1 -0
  96. package/dist/providers/base.d.ts +140 -0
  97. package/dist/providers/base.d.ts.map +1 -0
  98. package/dist/providers/base.js +2 -0
  99. package/dist/providers/base.js.map +1 -0
  100. package/dist/providers/deepseek.d.ts +118 -0
  101. package/dist/providers/deepseek.d.ts.map +1 -0
  102. package/dist/providers/deepseek.js +467 -0
  103. package/dist/providers/deepseek.js.map +1 -0
  104. package/dist/registry.d.ts +3 -0
  105. package/dist/registry.d.ts.map +1 -0
  106. package/dist/registry.js +94 -0
  107. package/dist/registry.js.map +1 -0
  108. package/dist/session-store.d.ts +133 -0
  109. package/dist/session-store.d.ts.map +1 -0
  110. package/dist/session-store.js +277 -0
  111. package/dist/session-store.js.map +1 -0
  112. package/dist/skills/curator.d.ts +104 -0
  113. package/dist/skills/curator.d.ts.map +1 -0
  114. package/dist/skills/curator.js +162 -0
  115. package/dist/skills/curator.js.map +1 -0
  116. package/dist/skills/index-builder.d.ts +42 -0
  117. package/dist/skills/index-builder.d.ts.map +1 -0
  118. package/dist/skills/index-builder.js +94 -0
  119. package/dist/skills/index-builder.js.map +1 -0
  120. package/dist/skills/loader.d.ts +107 -0
  121. package/dist/skills/loader.d.ts.map +1 -0
  122. package/dist/skills/loader.js +286 -0
  123. package/dist/skills/loader.js.map +1 -0
  124. package/dist/skills/preprocess.d.ts +45 -0
  125. package/dist/skills/preprocess.d.ts.map +1 -0
  126. package/dist/skills/preprocess.js +126 -0
  127. package/dist/skills/preprocess.js.map +1 -0
  128. package/dist/skills/usage.d.ts +75 -0
  129. package/dist/skills/usage.d.ts.map +1 -0
  130. package/dist/skills/usage.js +147 -0
  131. package/dist/skills/usage.js.map +1 -0
  132. package/dist/state/todos.d.ts +95 -0
  133. package/dist/state/todos.d.ts.map +1 -0
  134. package/dist/state/todos.js +198 -0
  135. package/dist/state/todos.js.map +1 -0
  136. package/dist/storage/conversations.d.ts +28 -0
  137. package/dist/storage/conversations.d.ts.map +1 -0
  138. package/dist/storage/conversations.js +8 -0
  139. package/dist/storage/conversations.js.map +1 -0
  140. package/dist/sub-agent/runner.d.ts +78 -0
  141. package/dist/sub-agent/runner.d.ts.map +1 -0
  142. package/dist/sub-agent/runner.js +215 -0
  143. package/dist/sub-agent/runner.js.map +1 -0
  144. package/dist/tools/builtin/agent.d.ts +33 -0
  145. package/dist/tools/builtin/agent.d.ts.map +1 -0
  146. package/dist/tools/builtin/agent.js +76 -0
  147. package/dist/tools/builtin/agent.js.map +1 -0
  148. package/dist/tools/builtin/bash.d.ts +11 -0
  149. package/dist/tools/builtin/bash.d.ts.map +1 -0
  150. package/dist/tools/builtin/bash.js +91 -0
  151. package/dist/tools/builtin/bash.js.map +1 -0
  152. package/dist/tools/builtin/edit.d.ts +21 -0
  153. package/dist/tools/builtin/edit.d.ts.map +1 -0
  154. package/dist/tools/builtin/edit.js +238 -0
  155. package/dist/tools/builtin/edit.js.map +1 -0
  156. package/dist/tools/builtin/read.d.ts +17 -0
  157. package/dist/tools/builtin/read.d.ts.map +1 -0
  158. package/dist/tools/builtin/read.js +139 -0
  159. package/dist/tools/builtin/read.js.map +1 -0
  160. package/dist/tools/builtin/sandbox.d.ts +16 -0
  161. package/dist/tools/builtin/sandbox.d.ts.map +1 -0
  162. package/dist/tools/builtin/sandbox.js +58 -0
  163. package/dist/tools/builtin/sandbox.js.map +1 -0
  164. package/dist/tools/builtin/skill_view.d.ts +37 -0
  165. package/dist/tools/builtin/skill_view.d.ts.map +1 -0
  166. package/dist/tools/builtin/skill_view.js +82 -0
  167. package/dist/tools/builtin/skill_view.js.map +1 -0
  168. package/dist/tools/builtin/todo_write.d.ts +29 -0
  169. package/dist/tools/builtin/todo_write.d.ts.map +1 -0
  170. package/dist/tools/builtin/todo_write.js +96 -0
  171. package/dist/tools/builtin/todo_write.js.map +1 -0
  172. package/dist/tools/builtin/web_fetch.d.ts +10 -0
  173. package/dist/tools/builtin/web_fetch.d.ts.map +1 -0
  174. package/dist/tools/builtin/web_fetch.js +150 -0
  175. package/dist/tools/builtin/web_fetch.js.map +1 -0
  176. package/dist/tools/builtin/write.d.ts +35 -0
  177. package/dist/tools/builtin/write.d.ts.map +1 -0
  178. package/dist/tools/builtin/write.js +70 -0
  179. package/dist/tools/builtin/write.js.map +1 -0
  180. package/dist/tools/file-state.d.ts +99 -0
  181. package/dist/tools/file-state.d.ts.map +1 -0
  182. package/dist/tools/file-state.js +133 -0
  183. package/dist/tools/file-state.js.map +1 -0
  184. package/dist/tools/hooks/sandbox-fs.d.ts +25 -0
  185. package/dist/tools/hooks/sandbox-fs.d.ts.map +1 -0
  186. package/dist/tools/hooks/sandbox-fs.js +48 -0
  187. package/dist/tools/hooks/sandbox-fs.js.map +1 -0
  188. package/dist/tools/registry.d.ts +102 -0
  189. package/dist/tools/registry.d.ts.map +1 -0
  190. package/dist/tools/registry.js +93 -0
  191. package/dist/tools/registry.js.map +1 -0
  192. package/dist/types.d.ts +109 -0
  193. package/dist/types.d.ts.map +1 -0
  194. package/dist/types.js +20 -0
  195. package/dist/types.js.map +1 -0
  196. package/package.json +72 -0
@@ -0,0 +1,147 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { DATA_DIR } from "../platform/config.js";
4
+ import { logger } from "../platform/logger.js";
5
+ const SCHEMA_VERSION = 1;
6
+ /** Default sidecar path. Overridable via `MAESTRO_SKILL_USAGE_PATH` for tests
7
+ * and operators that prefer a different on-disk location. */
8
+ export function defaultUsagePath() {
9
+ return (process.env.MAESTRO_SKILL_USAGE_PATH ??
10
+ join(DATA_DIR, "agents", "maestro", "skills", "usage.json"));
11
+ }
12
+ /** Mutex queue — each enqueued action awaits the previous one. */
13
+ const queuesByPath = new Map();
14
+ // Write-through cache: every `bump` is serialized through `enqueue`, so the
15
+ // in-process cache is the authoritative state between writes. Eliminates the
16
+ // per-turn readFileSync that curator + skill_view used to incur for every
17
+ // `loadUsage` call. Tests reset via `__resetForTests`.
18
+ const usageCacheByPath = new Map();
19
+ function enqueue(path, fn) {
20
+ const prev = queuesByPath.get(path) ?? Promise.resolve();
21
+ let resolver;
22
+ let rejecter;
23
+ const result = new Promise((res, rej) => {
24
+ resolver = res;
25
+ rejecter = rej;
26
+ });
27
+ const next = prev.then(async () => {
28
+ try {
29
+ const v = await fn();
30
+ resolver(v);
31
+ }
32
+ catch (err) {
33
+ rejecter(err);
34
+ }
35
+ });
36
+ queuesByPath.set(path, next);
37
+ return result;
38
+ }
39
+ function emptyFile() {
40
+ return { schemaVersion: SCHEMA_VERSION, skills: {} };
41
+ }
42
+ /**
43
+ * Read the usage file synchronously. Returns an empty (in-memory) file on
44
+ * ENOENT / parse failure — callers that need to disambiguate can call
45
+ * `existsSync(defaultUsagePath())` themselves. Logged at debug so a missing
46
+ * file is not noise on every read.
47
+ */
48
+ export function loadUsage(path = defaultUsagePath()) {
49
+ const cached = usageCacheByPath.get(path);
50
+ if (cached)
51
+ return cached;
52
+ const fresh = readUsageFromDisk(path);
53
+ usageCacheByPath.set(path, fresh);
54
+ return fresh;
55
+ }
56
+ function readUsageFromDisk(path) {
57
+ if (!existsSync(path))
58
+ return emptyFile();
59
+ let raw;
60
+ try {
61
+ raw = readFileSync(path, "utf8");
62
+ }
63
+ catch (err) {
64
+ logger.debug({ err, path }, "skill usage: read failed, returning empty");
65
+ return emptyFile();
66
+ }
67
+ try {
68
+ const parsed = JSON.parse(raw);
69
+ if (parsed.schemaVersion !== SCHEMA_VERSION || typeof parsed.skills !== "object") {
70
+ logger.warn({ path, schemaVersion: parsed.schemaVersion }, "skill usage: schema mismatch — resetting");
71
+ return emptyFile();
72
+ }
73
+ return parsed;
74
+ }
75
+ catch (err) {
76
+ logger.warn({ err, path }, "skill usage: JSON parse failed — resetting");
77
+ return emptyFile();
78
+ }
79
+ }
80
+ /** Get counters for a single skill. Never returns undefined — synthesizes
81
+ * zeroed counters for unseen skills so callers can compute deltas without
82
+ * null-checks. */
83
+ export function getCounters(name, path = defaultUsagePath()) {
84
+ const file = loadUsage(path);
85
+ return file.skills[name] ?? zeroedCounters();
86
+ }
87
+ function zeroedCounters() {
88
+ const ts = new Date().toISOString();
89
+ return {
90
+ viewCount: 0,
91
+ useCount: 0,
92
+ patchCount: 0,
93
+ lastTouchedTs: ts,
94
+ firstSeenTs: ts,
95
+ };
96
+ }
97
+ /** Atomic write: tmp → rename. Survives a process kill mid-write because
98
+ * the destination either has the old bytes or the new bytes, never half. */
99
+ function writeAtomic(path, contents) {
100
+ mkdirSync(dirname(path), { recursive: true });
101
+ const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
102
+ writeFileSync(tmp, JSON.stringify(contents, null, 2));
103
+ renameSync(tmp, path);
104
+ }
105
+ /** Internal: increment one counter by one for `name`, persisting. */
106
+ async function bump(name, key, path = defaultUsagePath()) {
107
+ if (!name) {
108
+ throw new Error("skill usage bump: empty name");
109
+ }
110
+ return enqueue(path, () => {
111
+ const file = loadUsage(path);
112
+ const ts = new Date().toISOString();
113
+ const existing = file.skills[name];
114
+ const next = existing
115
+ ? { ...existing, [key]: existing[key] + 1, lastTouchedTs: ts }
116
+ : { ...zeroedCounters(), firstSeenTs: ts, [key]: 1, lastTouchedTs: ts };
117
+ file.skills[name] = next;
118
+ writeAtomic(path, file);
119
+ // `file` is the cached reference — mutation above already updated the
120
+ // cache. Reassert here in case `loadUsage` had returned a fresh disk read
121
+ // (cold cache miss) so a subsequent loadUsage call gets the post-write
122
+ // state without going through disk again.
123
+ usageCacheByPath.set(path, file);
124
+ return next;
125
+ });
126
+ }
127
+ /** Bump `viewCount` for `name`. Called from `skill_view` after a successful body load. */
128
+ export function bumpView(name, path) {
129
+ return bump(name, "viewCount", path);
130
+ }
131
+ /** Bump `useCount` for `name`. Reserved for future call-site instrumentation. */
132
+ export function bumpUse(name, path) {
133
+ return bump(name, "useCount", path);
134
+ }
135
+ /** Bump `patchCount` for `name`. Reserved for the future `skill_edit` builtin. */
136
+ export function bumpPatch(name, path) {
137
+ return bump(name, "patchCount", path);
138
+ }
139
+ /** Test-only: drop the in-memory mutex queue + write-through cache. Disk
140
+ * file is NOT erased — tests that need filesystem isolation should point
141
+ * `MAESTRO_SKILL_USAGE_PATH` at a tmp file via `process.env` and remove it
142
+ * themselves. */
143
+ export function __resetForTests() {
144
+ queuesByPath.clear();
145
+ usageCacheByPath.clear();
146
+ }
147
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/skills/usage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Per-session todo list backing TodoWrite.
3
+ *
4
+ * The model gets a single `todo_write` tool to upsert / mutate the list. There
5
+ * is no `todo_list` tool — read-side surfaces via the per-turn system
6
+ * reminder, which keeps the model from juggling two near-identical tools
7
+ * (and saves a per-turn round-trip for what is fundamentally a snapshot view).
8
+ *
9
+ * Persistence: one JSON file at `~/.maestro/sessions/<sid>.todos.json` — a
10
+ * snapshot of the current list, atomically written. Not JSONL: `todo_write`
11
+ * semantically OVERWRITES the list each call, so an append-only stream
12
+ * would accumulate dead entries the loader has to filter. A single JSON
13
+ * snapshot matches the actual semantics and is cheaper to read.
14
+ *
15
+ * Lifecycle:
16
+ * - `getTodoStore(sid)` lazy-creates the store and hydrates from disk if
17
+ * a prior turn wrote one.
18
+ * - `dropTodoStore(sid)` is called from `deleteMaestroSession` and
19
+ * `cleanupStaleMaestroSessions` (same pattern as `file-state.ts`) so the
20
+ * module-level map can't outlive its sessions.
21
+ *
22
+ * Invariants enforced at upsert time:
23
+ * - At most one entry is `in_progress`. Setting a second one to
24
+ * `in_progress` flips any prior one to `pending` and warns the caller
25
+ * via the tool result. Mirrors Claude Code's TodoWrite contract.
26
+ * - IDs are auto-assigned (`task-1`, `task-2`, …) when omitted. Short IDs
27
+ * so the model can refer back to them in prose without bloat.
28
+ */
29
+ export type TodoStatus = "pending" | "in_progress" | "completed";
30
+ export interface TodoEntry {
31
+ id: string;
32
+ content: string;
33
+ status: TodoStatus;
34
+ /** Optional present-continuous form for spinner/status display
35
+ * ("Reading config" vs the imperative "Read config"). */
36
+ activeForm?: string;
37
+ }
38
+ export interface TodoUpsert {
39
+ id?: string;
40
+ content: string;
41
+ status: TodoStatus;
42
+ activeForm?: string;
43
+ }
44
+ export interface UpsertResult {
45
+ todos: TodoEntry[];
46
+ /** Set when the upsert had to flip a prior in_progress to pending to
47
+ * enforce the 1-in-progress invariant. The tool surfaces this in its
48
+ * result so the model knows the previously-active item was bumped. */
49
+ demotedId?: string;
50
+ }
51
+ export declare class TodoStore {
52
+ private readonly path;
53
+ private todos;
54
+ private nextCounter;
55
+ constructor(path: string);
56
+ /** Read the current list. Caller must not mutate. */
57
+ list(): readonly TodoEntry[];
58
+ /** True when the list is empty (no in-flight or pending work). */
59
+ isEmpty(): boolean;
60
+ /**
61
+ * Replace / upsert the entire list. Entries with an `id` that matches an
62
+ * existing one update that entry; entries without an id (or with an id
63
+ * not in the current list) become new entries with an auto-assigned id.
64
+ *
65
+ * The shape mirrors Claude Code's TodoWrite: the model passes a complete
66
+ * snapshot of "what I want the list to look like now," and we reconcile.
67
+ * That means an existing id NOT included in the incoming snapshot is
68
+ * dropped — TodoWrite is a snapshot replace, not a partial update.
69
+ *
70
+ * Returns `{todos, demotedId?}` so the tool can tell the model exactly
71
+ * what landed (incl. any in-progress demotion).
72
+ */
73
+ upsert(incoming: TodoUpsert[]): UpsertResult;
74
+ /** Drop the on-disk file. Called by deleteMaestroSession via dropTodoStore. */
75
+ unlinkFile(): void;
76
+ /** Test-only: snapshot the path the store is backed by. */
77
+ __path(): string;
78
+ private nextId;
79
+ private hydrate;
80
+ /** Recover the counter from existing IDs when the file lacked the field
81
+ * (defensive — recent hydrate writes always include it). */
82
+ private deriveCounter;
83
+ private persist;
84
+ }
85
+ /** Resolve the absolute todos-file path for a session. Mirrors
86
+ * `maestroSessionPath` but with `.todos.json` instead of `.jsonl`. */
87
+ export declare function todosPathFor(sessionId: string): string;
88
+ export declare function getTodoStore(sessionId: string): TodoStore;
89
+ /** Drop a session's store + unlink its on-disk file. */
90
+ export declare function dropTodoStore(sessionId: string): void;
91
+ /** Test-only. Reset every store. */
92
+ export declare function __resetAllStores(): void;
93
+ /** Test-only. Count of currently-tracked sessions. */
94
+ export declare function __storeCount(): number;
95
+ //# sourceMappingURL=todos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todos.d.ts","sourceRoot":"","sources":["../../src/state/todos.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;AAEjE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;IACnB;8DAC0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AASD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;;2EAEuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,SAAS;IAIR,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,WAAW,CAAK;gBAEK,IAAI,EAAE,MAAM;IAIzC,qDAAqD;IACrD,IAAI,IAAI,SAAS,SAAS,EAAE;IAI5B,kEAAkE;IAClE,OAAO,IAAI,OAAO;IAIlB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,YAAY;IAwC5C,+EAA+E;IAC/E,UAAU,IAAI,IAAI;IAUlB,2DAA2D;IAC3D,MAAM,IAAI,MAAM;IAMhB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,OAAO;IAgBf;iEAC6D;IAC7D,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,OAAO;CAQhB;AAqBD;uEACuE;AACvE,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtD;AAeD,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAOzD;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAMrD;AAED,oCAAoC;AACpC,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,sDAAsD;AACtD,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
@@ -0,0 +1,198 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { logger } from "../platform/logger.js";
5
+ /** Mirror of `maestroSessionsDir()` from session-store.ts — inlined here to
6
+ * avoid a circular import (session-store.ts needs to call
7
+ * `dropTodoStore` for cleanup). Two single-line copies are cheaper than
8
+ * extracting a shared util file for one path. */
9
+ function sessionsDir() {
10
+ return join(homedir(), ".maestro", "sessions");
11
+ }
12
+ export class TodoStore {
13
+ path;
14
+ todos = [];
15
+ nextCounter = 1;
16
+ constructor(path) {
17
+ this.path = path;
18
+ this.hydrate();
19
+ }
20
+ /** Read the current list. Caller must not mutate. */
21
+ list() {
22
+ return this.todos;
23
+ }
24
+ /** True when the list is empty (no in-flight or pending work). */
25
+ isEmpty() {
26
+ return this.todos.length === 0;
27
+ }
28
+ /**
29
+ * Replace / upsert the entire list. Entries with an `id` that matches an
30
+ * existing one update that entry; entries without an id (or with an id
31
+ * not in the current list) become new entries with an auto-assigned id.
32
+ *
33
+ * The shape mirrors Claude Code's TodoWrite: the model passes a complete
34
+ * snapshot of "what I want the list to look like now," and we reconcile.
35
+ * That means an existing id NOT included in the incoming snapshot is
36
+ * dropped — TodoWrite is a snapshot replace, not a partial update.
37
+ *
38
+ * Returns `{todos, demotedId?}` so the tool can tell the model exactly
39
+ * what landed (incl. any in-progress demotion).
40
+ */
41
+ upsert(incoming) {
42
+ const out = [];
43
+ let demotedId;
44
+ // First pass: build the new list, assigning IDs to entries that need
45
+ // them. We do this BEFORE the 1-in-progress sweep so the auto-IDs are
46
+ // available for the warning message.
47
+ for (const item of incoming) {
48
+ const id = item.id?.trim() || this.nextId();
49
+ out.push({
50
+ id,
51
+ content: item.content,
52
+ status: item.status,
53
+ ...(item.activeForm ? { activeForm: item.activeForm } : {}),
54
+ });
55
+ }
56
+ // Second pass: enforce 1-in-progress. Walk in incoming order; the LAST
57
+ // in_progress wins (mirrors how a model batch-updates and the final
58
+ // entry expresses current intent). Earlier in_progress entries are
59
+ // flipped to pending and surfaced via `demotedId`.
60
+ let lastInProgressIdx = -1;
61
+ for (let i = 0; i < out.length; i++) {
62
+ if (out[i].status === "in_progress")
63
+ lastInProgressIdx = i;
64
+ }
65
+ if (lastInProgressIdx >= 0) {
66
+ for (let i = 0; i < out.length; i++) {
67
+ if (i === lastInProgressIdx)
68
+ continue;
69
+ if (out[i].status === "in_progress") {
70
+ out[i] = { ...out[i], status: "pending" };
71
+ demotedId = out[i].id;
72
+ }
73
+ }
74
+ }
75
+ this.todos = out;
76
+ this.persist();
77
+ return demotedId !== undefined ? { todos: out, demotedId } : { todos: out };
78
+ }
79
+ /** Drop the on-disk file. Called by deleteMaestroSession via dropTodoStore. */
80
+ unlinkFile() {
81
+ try {
82
+ unlinkSync(this.path);
83
+ }
84
+ catch (e) {
85
+ if (e?.code !== "ENOENT") {
86
+ logger.warn({ err: e, path: this.path }, "TodoStore.unlinkFile failed");
87
+ }
88
+ }
89
+ }
90
+ /** Test-only: snapshot the path the store is backed by. */
91
+ __path() {
92
+ return this.path;
93
+ }
94
+ // --- internals ---------------------------------------------------------
95
+ nextId() {
96
+ const id = `task-${this.nextCounter}`;
97
+ this.nextCounter++;
98
+ return id;
99
+ }
100
+ hydrate() {
101
+ if (!existsSync(this.path))
102
+ return;
103
+ try {
104
+ const raw = readFileSync(this.path, "utf8");
105
+ const parsed = JSON.parse(raw);
106
+ if (parsed.version !== 1 || !Array.isArray(parsed.todos)) {
107
+ logger.warn({ path: this.path }, "TodoStore.hydrate: unsupported schema, ignoring");
108
+ return;
109
+ }
110
+ this.todos = parsed.todos.filter(isWellFormedEntry);
111
+ this.nextCounter = Math.max(1, parsed.nextCounter ?? this.deriveCounter());
112
+ }
113
+ catch (e) {
114
+ logger.warn({ err: e, path: this.path }, "TodoStore.hydrate: parse failed, starting empty");
115
+ }
116
+ }
117
+ /** Recover the counter from existing IDs when the file lacked the field
118
+ * (defensive — recent hydrate writes always include it). */
119
+ deriveCounter() {
120
+ let max = 0;
121
+ for (const t of this.todos) {
122
+ const m = /^task-(\d+)$/.exec(t.id);
123
+ if (m)
124
+ max = Math.max(max, Number.parseInt(m[1], 10));
125
+ }
126
+ return max + 1;
127
+ }
128
+ persist() {
129
+ const file = {
130
+ version: 1,
131
+ nextCounter: this.nextCounter,
132
+ todos: this.todos,
133
+ };
134
+ writeAtomic(this.path, file);
135
+ }
136
+ }
137
+ /** Atomic write: tmp → rename. Same pattern as skills/usage.ts. */
138
+ function writeAtomic(path, contents) {
139
+ mkdirSync(dirname(path), { recursive: true });
140
+ const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
141
+ writeFileSync(tmp, JSON.stringify(contents, null, 2));
142
+ renameSync(tmp, path);
143
+ }
144
+ function isWellFormedEntry(v) {
145
+ if (!v || typeof v !== "object")
146
+ return false;
147
+ const o = v;
148
+ if (typeof o.id !== "string" || typeof o.content !== "string")
149
+ return false;
150
+ if (o.status !== "pending" && o.status !== "in_progress" && o.status !== "completed") {
151
+ return false;
152
+ }
153
+ if (o.activeForm !== undefined && typeof o.activeForm !== "string")
154
+ return false;
155
+ return true;
156
+ }
157
+ /** Resolve the absolute todos-file path for a session. Mirrors
158
+ * `maestroSessionPath` but with `.todos.json` instead of `.jsonl`. */
159
+ export function todosPathFor(sessionId) {
160
+ return join(sessionsDir(), `${sessionId}.todos.json`);
161
+ }
162
+ /**
163
+ * Module-level registry — same pattern as `file-state.ts`.
164
+ *
165
+ * The maestroProvider rebuilds tools each turn but the sessionId persists
166
+ * across turns. A registry-local store would re-hydrate every turn (cheap
167
+ * but wasteful); a module-level cache keeps the in-memory list hot.
168
+ *
169
+ * Cleanup is wired into `deleteMaestroSession` and
170
+ * `cleanupStaleMaestroSessions` (see session-store.ts) so the map can't
171
+ * outlive its sessions.
172
+ */
173
+ const stores = new Map();
174
+ export function getTodoStore(sessionId) {
175
+ let s = stores.get(sessionId);
176
+ if (!s) {
177
+ s = new TodoStore(todosPathFor(sessionId));
178
+ stores.set(sessionId, s);
179
+ }
180
+ return s;
181
+ }
182
+ /** Drop a session's store + unlink its on-disk file. */
183
+ export function dropTodoStore(sessionId) {
184
+ const s = stores.get(sessionId);
185
+ if (s) {
186
+ s.unlinkFile();
187
+ stores.delete(sessionId);
188
+ }
189
+ }
190
+ /** Test-only. Reset every store. */
191
+ export function __resetAllStores() {
192
+ stores.clear();
193
+ }
194
+ /** Test-only. Count of currently-tracked sessions. */
195
+ export function __storeCount() {
196
+ return stores.size;
197
+ }
198
+ //# sourceMappingURL=todos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todos.js","sourceRoot":"","sources":["../../src/state/todos.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { AgentKind, UnifiedEvent } from "../types.js";
2
+ /**
3
+ * Provider-agnostic conversation entry.
4
+ *
5
+ * The SDK does **not** persist conversations itself — that's the host's job.
6
+ * This interface is exposed so hosts can hand the SDK a back-fill of past
7
+ * turns (`registry.forkSession` rehydrates from this shape) and so SDK
8
+ * code that needs to encode/decode rollout entries shares a single type.
9
+ */
10
+ export interface ConversationEntry {
11
+ /** ISO timestamp the event was recorded. */
12
+ ts: string;
13
+ /** Agent that produced this event, frozen at write time. */
14
+ agent: AgentKind;
15
+ /** Normalized event payload. */
16
+ event: UnifiedEvent;
17
+ }
18
+ /**
19
+ * Host-supplied callback that returns the conversation log for a session.
20
+ *
21
+ * The SDK calls this from `registry.forkSession()` when synthesizing a maestro
22
+ * rollout from cross-agent history. Hosts that don't need cross-agent
23
+ * bridging can leave the default (returns `[]`) in place.
24
+ */
25
+ export type ConversationReader = (userId: number | string, topicName: string, groupId?: number) => ConversationEntry[];
26
+ export declare function setConversationReader(reader: ConversationReader): void;
27
+ export declare function readConversation(userId: number | string, topicName: string, groupId?: number): ConversationEntry[];
28
+ //# sourceMappingURL=conversations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/storage/conversations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,KAAK,EAAE,SAAS,CAAC;IACjB,gCAAgC;IAChC,KAAK,EAAE,YAAY,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAC/B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,KACb,iBAAiB,EAAE,CAAC;AAIzB,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAEtE;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,iBAAiB,EAAE,CAErB"}
@@ -0,0 +1,8 @@
1
+ let _reader = () => [];
2
+ export function setConversationReader(reader) {
3
+ _reader = reader;
4
+ }
5
+ export function readConversation(userId, topicName, groupId) {
6
+ return _reader(userId, topicName, groupId);
7
+ }
8
+ //# sourceMappingURL=conversations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversations.js","sourceRoot":"","sources":["../../src/storage/conversations.ts"],"names":[],"mappings":"AAgCA,IAAI,OAAO,GAAuB,GAAG,EAAE,CAAC,EAAE,CAAC;AAE3C,MAAM,UAAU,qBAAqB,CAAC,MAA0B;IAC9D,OAAO,GAAG,MAAM,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,MAAuB,EACvB,SAAiB,EACjB,OAAgB;IAEhB,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { loadSkillsCached, type SkillEntry } from "../skills/loader.js";
2
+ import type { EffortLevel, TokenUsage } from "../types.js";
3
+ /**
4
+ * Sub-agent runner — spawns a fresh maestro loop for a delegated task.
5
+ *
6
+ * Architecture decisions (advisor-reviewed):
7
+ * - **No MCP in v1.** Sub-agents are scoped roles, not parent-lite. If
8
+ * MCP-rich exploration is needed, the user spawns a new topic.
9
+ * - **Inherit parent model + effort.** Uses `providerForModel()` to
10
+ * select the correct provider (Anthropic / DeepSeek) based on parent
11
+ * model. No override knob — keeps the model from picking a smaller
12
+ * model "to save cost" and missing things.
13
+ * - **Separate file-state tracker per sub-session.** Sharing the parent's
14
+ * would break the Read-before-Edit invariant in both directions.
15
+ * - **Inherit parent's systemPrompt + overlay.** Parent persona is the
16
+ * base; the sub-agent prompt adds the "you are a sub-agent" role.
17
+ * - **Hard JSONL cleanup on RETURN (success/failure).** NOT on abort —
18
+ * a parent abort mid-sub-call leaves the JSONL for postmortem and
19
+ * the 30-day TTL sweep eventually clears it.
20
+ * - **Silent by default.** Parent's event stream only carries the Agent
21
+ * tool's `tool_use` + final text. The sub-agent's text_deltas /
22
+ * tool_uses are NOT forwarded to telegram.
23
+ * - **depth=1 cap.** Sub-agents do NOT get the Agent tool — implicit
24
+ * because runSubAgent doesn't register one in the child registry.
25
+ * No recursion. Grandchildren require the parent to re-delegate.
26
+ */
27
+ export type SubagentType = "general" | "explore";
28
+ export interface RunSubAgentOptions {
29
+ subagentType: SubagentType;
30
+ /** Self-contained task brief. The sub-agent sees only this as the user
31
+ * message. */
32
+ prompt: string;
33
+ /** Parent's resolved maestro sessionId. Used only for logging — the
34
+ * sub-agent has its own freshly-minted session id. */
35
+ parentSessionId: string;
36
+ /** Parent's augmented systemPrompt (caller-prompt + skills index).
37
+ * Used as the base layer of the sub-agent's prompt. */
38
+ parentSystemPrompt: string;
39
+ /** Parent's resolved model id (full, alias already expanded). */
40
+ parentModel: string;
41
+ /** Parent's effort level. Passed through to the provider (Anthropic:
42
+ * thinking budget; DeepSeek: reasoning_effort). */
43
+ parentEffort?: EffortLevel;
44
+ /** Parent's abort signal. When the parent aborts, the sub-agent's
45
+ * in-flight provider call cancels too. */
46
+ parentAbortSignal?: AbortSignal;
47
+ /** Pre-loaded skill catalog. Same set the parent sees. */
48
+ skills: SkillEntry[];
49
+ }
50
+ export interface RunSubAgentResult {
51
+ /** The sub-agent's final assistant text. This becomes the Agent tool's
52
+ * return value to the parent model. */
53
+ text: string;
54
+ /** Accumulated usage across every API call the sub-agent made. */
55
+ usage: TokenUsage;
56
+ /** The sub-agent's own session id. Surfaced for diagnostics + so the
57
+ * Agent tool can include it in the result text for traceability. */
58
+ subSessionId: string;
59
+ /** True when the parent's abort signal fired mid-execution. */
60
+ aborted: boolean;
61
+ }
62
+ /**
63
+ * Run a sub-agent to completion. Returns its final text + accumulated
64
+ * usage; the caller (Agent tool) decides how to surface that to the
65
+ * parent model.
66
+ *
67
+ * Cleanup contract (load-bearing):
68
+ * - Clean drain or thrown error → `deleteMaestroSession(subSessionId)`
69
+ * in the finally block. Frees the JSONL + file-state tracker + (any)
70
+ * todo store for this sub-session.
71
+ * - Abort → leave JSONL on disk. The 30-day TTL sweep will eventually
72
+ * clear it; meanwhile it's available for postmortem.
73
+ */
74
+ export declare function runSubAgent(opts: RunSubAgentOptions): Promise<RunSubAgentResult>;
75
+ /** Convenience re-export for test setup — lets tests prime the loaded
76
+ * catalog without re-fetching from disk. */
77
+ export { loadSkillsCached };
78
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/sub-agent/runner.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAWpE,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;IAC3B;mBACe;IACf,MAAM,EAAE,MAAM,CAAC;IACf;2DACuD;IACvD,eAAe,EAAE,MAAM,CAAC;IACxB;4DACwD;IACxD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB;yDACqD;IACrD,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B;+CAC2C;IAC3C,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAChC,0DAA0D;IAC1D,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC;4CACwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,KAAK,EAAE,UAAU,CAAC;IAClB;yEACqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,OAAO,EAAE,OAAO,CAAC;CAClB;AAyED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+GtF;AAgBD;6CAC6C;AAC7C,OAAO,EAAE,gBAAgB,EAAE,CAAC"}