@mastra/code-sdk 1.15.0-alpha.1 → 1.15.0-alpha.11
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/acp/agent.d.ts.map +1 -1
- package/dist/acp/agent.js +1 -1
- package/dist/acp/agent.js.map +1 -1
- package/dist/acp/event-mapper.d.ts.map +1 -1
- package/dist/acp/event-mapper.js +1 -1
- package/dist/acp/event-mapper.js.map +1 -1
- package/dist/agent-connections/signal-provider.d.ts +1 -1
- package/dist/agent-connections/signal-provider.d.ts.map +1 -1
- package/dist/agent-connections/signal-provider.js.map +1 -1
- package/dist/agent-connections/state-processor.d.ts +2 -2
- package/dist/agent-connections/state-processor.d.ts.map +1 -1
- package/dist/agent-connections/state-processor.js.map +1 -1
- package/dist/agent-connections/thread-state.d.ts +1 -1
- package/dist/agent-connections/thread-state.d.ts.map +1 -1
- package/dist/agent-connections/thread-state.js.map +1 -1
- package/dist/agent-connections/tools.d.ts.map +1 -1
- package/dist/agent-connections/tools.js +1 -1
- package/dist/agent-connections/tools.js.map +1 -1
- package/dist/agents/background-completion.d.ts +15 -2
- package/dist/agents/background-completion.d.ts.map +1 -1
- package/dist/agents/background-completion.js +19 -2
- package/dist/agents/background-completion.js.map +1 -1
- package/dist/agents/credential-resolver.d.ts +6 -0
- package/dist/agents/credential-resolver.d.ts.map +1 -1
- package/dist/agents/credential-resolver.js +13 -4
- package/dist/agents/credential-resolver.js.map +1 -1
- package/dist/agents/memory.d.ts.map +1 -1
- package/dist/agents/memory.js +1 -1
- package/dist/agents/memory.js.map +1 -1
- package/dist/agents/sandbox-filesystem.d.ts +32 -10
- package/dist/agents/sandbox-filesystem.d.ts.map +1 -1
- package/dist/agents/sandbox-filesystem.js +208 -66
- package/dist/agents/sandbox-filesystem.js.map +1 -1
- package/dist/agents/workspace.d.ts.map +1 -1
- package/dist/agents/workspace.js +1 -0
- package/dist/agents/workspace.js.map +1 -1
- package/dist/experimental-agent.d.ts +1 -1
- package/dist/experimental-agent.d.ts.map +1 -1
- package/dist/experimental-agent.js.map +1 -1
- package/dist/factory/client.d.ts +41 -0
- package/dist/factory/client.d.ts.map +1 -0
- package/dist/factory/client.js +98 -0
- package/dist/factory/client.js.map +1 -0
- package/dist/factory/remote-controller.d.ts +34 -0
- package/dist/factory/remote-controller.d.ts.map +1 -0
- package/dist/factory/remote-controller.js +43 -0
- package/dist/factory/remote-controller.js.map +1 -0
- package/dist/factory/remote-session.d.ts +210 -0
- package/dist/factory/remote-session.d.ts.map +1 -0
- package/dist/factory/remote-session.js +420 -0
- package/dist/factory/remote-session.js.map +1 -0
- package/dist/headless/cli.d.ts +7 -0
- package/dist/headless/cli.d.ts.map +1 -1
- package/dist/headless/cli.js +19 -3
- package/dist/headless/cli.js.map +1 -1
- package/dist/headless/flags.d.ts.map +1 -1
- package/dist/headless/flags.js +2 -1
- package/dist/headless/flags.js.map +1 -1
- package/dist/headless/format.d.ts +2 -0
- package/dist/headless/format.d.ts.map +1 -1
- package/dist/headless/format.js +13 -1
- package/dist/headless/format.js.map +1 -1
- package/dist/headless/run-mc.d.ts.map +1 -1
- package/dist/headless/run-mc.js +48 -20
- package/dist/headless/run-mc.js.map +1 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -25
- package/dist/index.js.map +1 -1
- package/dist/onboarding/pack-activation.d.ts +2 -11
- package/dist/onboarding/pack-activation.d.ts.map +1 -1
- package/dist/onboarding/pack-activation.js.map +1 -1
- package/dist/onboarding/settings.d.ts +18 -0
- package/dist/onboarding/settings.d.ts.map +1 -1
- package/dist/onboarding/settings.js +28 -3
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/platform/index.d.ts +74 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +200 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/process-memory-diagnostics.d.ts +2 -1
- package/dist/process-memory-diagnostics.d.ts.map +1 -1
- package/dist/process-memory-diagnostics.js.map +1 -1
- package/dist/providers/model-ids.d.ts +1 -0
- package/dist/providers/model-ids.d.ts.map +1 -1
- package/dist/providers/model-ids.js +6 -4
- package/dist/providers/model-ids.js.map +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js.map +1 -1
- package/dist/tools/workflows/create-workflow.d.ts.map +1 -1
- package/dist/tools/workflows/create-workflow.js +5 -1
- package/dist/tools/workflows/create-workflow.js.map +1 -1
- package/dist/utils/local-observability.d.ts +19 -0
- package/dist/utils/local-observability.d.ts.map +1 -0
- package/dist/utils/local-observability.js +57 -0
- package/dist/utils/local-observability.js.map +1 -0
- package/dist/utils/notification-dispatch.d.ts +27 -12
- package/dist/utils/notification-dispatch.d.ts.map +1 -1
- package/dist/utils/notification-dispatch.js +46 -24
- package/dist/utils/notification-dispatch.js.map +1 -1
- package/dist/utils/signals-pubsub.d.ts +5 -3
- package/dist/utils/signals-pubsub.d.ts.map +1 -1
- package/dist/utils/signals-pubsub.js +8 -6
- package/dist/utils/signals-pubsub.js.map +1 -1
- package/dist/utils/wake-session.d.ts +44 -0
- package/dist/utils/wake-session.d.ts.map +1 -0
- package/dist/utils/wake-session.js +86 -0
- package/dist/utils/wake-session.js.map +1 -0
- package/dist/workflows/register-primitives.js +4 -1
- package/dist/workflows/register-primitives.js.map +1 -1
- package/package.json +23 -23
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { createFactoryClient } from "./client.js";
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
import { defaultDisplayState } from "@mastra/core/agent-controller";
|
|
4
|
+
//#region src/factory/remote-session.ts
|
|
5
|
+
const MAP_FIELDS = [
|
|
6
|
+
"activeTools",
|
|
7
|
+
"toolInputBuffers",
|
|
8
|
+
"pendingSuspensions",
|
|
9
|
+
"pendingApprovals",
|
|
10
|
+
"activeSubagents",
|
|
11
|
+
"modifiedFiles"
|
|
12
|
+
];
|
|
13
|
+
/** The wire flattens the display state's Maps to objects; restore them. */
|
|
14
|
+
function hydrateDisplayState(wire) {
|
|
15
|
+
const state = {
|
|
16
|
+
...defaultDisplayState(),
|
|
17
|
+
...wire
|
|
18
|
+
};
|
|
19
|
+
for (const field of MAP_FIELDS) {
|
|
20
|
+
const value = wire[field];
|
|
21
|
+
state[field] = value instanceof Map ? value : new Map(Object.entries(value ?? {}));
|
|
22
|
+
}
|
|
23
|
+
return state;
|
|
24
|
+
}
|
|
25
|
+
/** The wire flattens errors to `{ name, message }`; restore them. */
|
|
26
|
+
function hydrateEvent(event) {
|
|
27
|
+
const out = { ...event };
|
|
28
|
+
if (out.displayState && typeof out.displayState === "object") out.displayState = hydrateDisplayState(out.displayState);
|
|
29
|
+
const error = out.error;
|
|
30
|
+
if (error && typeof error === "object" && !(error instanceof Error)) out.error = Object.assign(new Error(error.message ?? "Remote error"), { name: error.name ?? "Error" });
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function toThread(thread, resourceId) {
|
|
34
|
+
const date = (value) => value ? new Date(value) : /* @__PURE__ */ new Date();
|
|
35
|
+
return {
|
|
36
|
+
id: thread.id,
|
|
37
|
+
resourceId: thread.resourceId ?? resourceId,
|
|
38
|
+
title: thread.title ?? "",
|
|
39
|
+
createdAt: date(thread.createdAt),
|
|
40
|
+
updatedAt: date(thread.updatedAt)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Stands in for an in-process `Session` while the agent, tools, and storage
|
|
45
|
+
* run inside a Factory. Reads are served from state mirrored off the session's
|
|
46
|
+
* event stream; every action is forwarded to the Factory over HTTP.
|
|
47
|
+
*
|
|
48
|
+
* Only the surface the TUI drives is implemented. Anything else resolves to a
|
|
49
|
+
* no-op so local-only features (thread locks, grants) degrade instead of crash.
|
|
50
|
+
*/
|
|
51
|
+
var RemoteSession = class {
|
|
52
|
+
#remote;
|
|
53
|
+
#sessionId;
|
|
54
|
+
/** Whose threads and memory the session works on; the Factory reports it when the session is created. */
|
|
55
|
+
#resourceId;
|
|
56
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
57
|
+
#unsubscribe;
|
|
58
|
+
#threadId = null;
|
|
59
|
+
#modeId = "";
|
|
60
|
+
#modelId = "";
|
|
61
|
+
#running = false;
|
|
62
|
+
#state = {};
|
|
63
|
+
#displayState = defaultDisplayState();
|
|
64
|
+
#modes = [];
|
|
65
|
+
#permissionRules = {
|
|
66
|
+
categories: {},
|
|
67
|
+
tools: {}
|
|
68
|
+
};
|
|
69
|
+
#pendingUserIds = [];
|
|
70
|
+
#userIdMap = /* @__PURE__ */ new Map();
|
|
71
|
+
constructor(address, sessionId, remote) {
|
|
72
|
+
this.#sessionId = sessionId;
|
|
73
|
+
this.#resourceId = sessionId;
|
|
74
|
+
this.#remote = remote ?? createFactoryClient(address).getAgentController("code").session(sessionId);
|
|
75
|
+
}
|
|
76
|
+
/** Create/resume the remote session, hydrate the mirror, and open the event stream. */
|
|
77
|
+
async connect() {
|
|
78
|
+
const created = await this.#remote.create();
|
|
79
|
+
if (created?.resourceId) this.#resourceId = created.resourceId;
|
|
80
|
+
await Promise.all([this.#hydrate(), this.#refreshPermissions()]);
|
|
81
|
+
const subscription = await this.#remote.subscribe({
|
|
82
|
+
reconnect: true,
|
|
83
|
+
onEvent: (event) => this.#receive(hydrateEvent(event)),
|
|
84
|
+
onReconnect: () => void this.#hydrate().catch(() => {})
|
|
85
|
+
});
|
|
86
|
+
this.#unsubscribe = subscription.unsubscribe;
|
|
87
|
+
}
|
|
88
|
+
/** Modes come from the controller; used to resolve the current mode's name and color. */
|
|
89
|
+
setModes(modes) {
|
|
90
|
+
this.#modes = modes;
|
|
91
|
+
}
|
|
92
|
+
disconnect() {
|
|
93
|
+
this.#unsubscribe?.();
|
|
94
|
+
this.#unsubscribe = void 0;
|
|
95
|
+
}
|
|
96
|
+
async #hydrate() {
|
|
97
|
+
const snapshot = await this.#remote.state();
|
|
98
|
+
this.#threadId = snapshot.threadId ?? null;
|
|
99
|
+
this.#modeId = snapshot.modeId;
|
|
100
|
+
this.#modelId = snapshot.modelId;
|
|
101
|
+
this.#running = Boolean(snapshot.running);
|
|
102
|
+
if (snapshot.settings) this.#state = {
|
|
103
|
+
...this.#state,
|
|
104
|
+
...snapshot.settings
|
|
105
|
+
};
|
|
106
|
+
this.#displayState = {
|
|
107
|
+
...this.#displayState,
|
|
108
|
+
...snapshot.tasks ? { tasks: snapshot.tasks } : {},
|
|
109
|
+
...snapshot.tokenUsage ? { tokenUsage: snapshot.tokenUsage } : {}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async #refreshPermissions() {
|
|
113
|
+
const rules = await this.#remote.getPermissions();
|
|
114
|
+
this.#permissionRules = {
|
|
115
|
+
categories: rules.categories ?? {},
|
|
116
|
+
tools: rules.tools ?? {}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** Surface a failed remote command in the transcript instead of dropping it. */
|
|
120
|
+
#emitError(message) {
|
|
121
|
+
this.#receive({
|
|
122
|
+
type: "error",
|
|
123
|
+
error: new Error(message)
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The TUI renders a user message as soon as it is sent, keyed by the id
|
|
128
|
+
* `sendSignal` returned. The Factory echoes it back under its own id, so map
|
|
129
|
+
* that echo onto the local id to keep it from rendering twice.
|
|
130
|
+
*/
|
|
131
|
+
#adoptLocalUserId(e) {
|
|
132
|
+
const message = e.message;
|
|
133
|
+
const isUser = message?.role === "user" || message?.role === "signal" && message.content?.metadata?.signal?.type === "user";
|
|
134
|
+
if (!message?.id || !isUser) return;
|
|
135
|
+
let localId = this.#userIdMap.get(message.id);
|
|
136
|
+
if (!localId && e.type === "message_start") {
|
|
137
|
+
localId = this.#pendingUserIds.shift();
|
|
138
|
+
if (localId) this.#userIdMap.set(message.id, localId);
|
|
139
|
+
}
|
|
140
|
+
if (localId) e.message = {
|
|
141
|
+
...message,
|
|
142
|
+
id: localId
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
#receive(event) {
|
|
146
|
+
const e = event;
|
|
147
|
+
if (e.type === "message_start" || e.type === "message_update" || e.type === "message_end") this.#adoptLocalUserId(e);
|
|
148
|
+
switch (e.type) {
|
|
149
|
+
case "mode_changed":
|
|
150
|
+
this.#modeId = e.modeId;
|
|
151
|
+
break;
|
|
152
|
+
case "model_changed":
|
|
153
|
+
this.#modelId = e.modelId;
|
|
154
|
+
break;
|
|
155
|
+
case "thread_changed":
|
|
156
|
+
this.#threadId = e.threadId;
|
|
157
|
+
break;
|
|
158
|
+
case "thread_created":
|
|
159
|
+
this.#threadId = e.thread.id;
|
|
160
|
+
break;
|
|
161
|
+
case "state_changed":
|
|
162
|
+
this.#state = {
|
|
163
|
+
...this.#state,
|
|
164
|
+
...e.state
|
|
165
|
+
};
|
|
166
|
+
break;
|
|
167
|
+
case "display_state_changed":
|
|
168
|
+
this.#displayState = e.displayState;
|
|
169
|
+
break;
|
|
170
|
+
case "agent_start":
|
|
171
|
+
this.#running = true;
|
|
172
|
+
break;
|
|
173
|
+
case "agent_end":
|
|
174
|
+
this.#running = false;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
for (const listener of this.#listeners) Promise.resolve(listener(event)).catch(() => {});
|
|
178
|
+
}
|
|
179
|
+
subscribe(listener) {
|
|
180
|
+
this.#listeners.add(listener);
|
|
181
|
+
return () => this.#listeners.delete(listener);
|
|
182
|
+
}
|
|
183
|
+
onBeforeAgentEnd() {
|
|
184
|
+
return () => {};
|
|
185
|
+
}
|
|
186
|
+
identity = {
|
|
187
|
+
getResourceId: () => this.#resourceId,
|
|
188
|
+
getDefaultResourceId: () => this.#resourceId,
|
|
189
|
+
getId: () => this.#sessionId,
|
|
190
|
+
getOwnerId: () => this.#sessionId,
|
|
191
|
+
setResourceId: () => {}
|
|
192
|
+
};
|
|
193
|
+
thread = (() => {
|
|
194
|
+
const remote = () => this.#remote;
|
|
195
|
+
const resourceId = () => this.#resourceId;
|
|
196
|
+
return {
|
|
197
|
+
getId: () => this.#threadId,
|
|
198
|
+
isSet: () => this.#threadId !== null,
|
|
199
|
+
requireId: () => {
|
|
200
|
+
if (!this.#threadId) throw new Error("No active thread");
|
|
201
|
+
return this.#threadId;
|
|
202
|
+
},
|
|
203
|
+
ensureId: async () => {
|
|
204
|
+
if (!this.#threadId) await this.thread.create();
|
|
205
|
+
return this.#threadId;
|
|
206
|
+
},
|
|
207
|
+
list: async () => (await remote().listThreads()).map((t) => toThread(t, resourceId())),
|
|
208
|
+
getById: async ({ threadId }) => (await this.thread.list()).find((t) => t.id === threadId) ?? null,
|
|
209
|
+
create: async ({ title } = {}) => {
|
|
210
|
+
const thread = toThread(await remote().createThread(title), resourceId());
|
|
211
|
+
this.#threadId = thread.id;
|
|
212
|
+
return thread;
|
|
213
|
+
},
|
|
214
|
+
switch: async ({ threadId }) => {
|
|
215
|
+
await remote().switchThread(threadId);
|
|
216
|
+
this.#threadId = threadId;
|
|
217
|
+
},
|
|
218
|
+
clone: async ({ sourceThreadId, title } = {}) => {
|
|
219
|
+
const thread = toThread(await remote().cloneThread({
|
|
220
|
+
sourceThreadId,
|
|
221
|
+
title
|
|
222
|
+
}), resourceId());
|
|
223
|
+
this.#threadId = thread.id;
|
|
224
|
+
return thread;
|
|
225
|
+
},
|
|
226
|
+
rename: async ({ title }) => {
|
|
227
|
+
if (this.#threadId) await remote().renameThread(this.#threadId, title);
|
|
228
|
+
},
|
|
229
|
+
delete: async ({ threadId }) => {
|
|
230
|
+
await remote().deleteThread(threadId);
|
|
231
|
+
if (this.#threadId === threadId) this.#threadId = null;
|
|
232
|
+
},
|
|
233
|
+
listMessages: ({ threadId, limit }) => remote().listMessages(threadId, limit),
|
|
234
|
+
listActiveMessages: async ({ limit } = {}) => this.#threadId ? remote().listMessages(this.#threadId, limit) : [],
|
|
235
|
+
firstUserMessage: async () => null,
|
|
236
|
+
firstUserMessages: async () => /* @__PURE__ */ new Map(),
|
|
237
|
+
getSetting: async () => void 0,
|
|
238
|
+
getSettingOn: async () => void 0,
|
|
239
|
+
setSetting: async () => {},
|
|
240
|
+
setSettingOn: async () => {},
|
|
241
|
+
deleteSetting: async () => {},
|
|
242
|
+
cleanupSubscription: () => {},
|
|
243
|
+
detachFromCurrent: () => {},
|
|
244
|
+
clear: () => {
|
|
245
|
+
this.#threadId = null;
|
|
246
|
+
},
|
|
247
|
+
clearAndReleaseLock: async () => {},
|
|
248
|
+
cloneToCurrentResource: async ({ sourceThreadId }) => this.thread.clone({ sourceThreadId }),
|
|
249
|
+
loadMetadata: async () => {}
|
|
250
|
+
};
|
|
251
|
+
})();
|
|
252
|
+
mode = {
|
|
253
|
+
get: () => this.#modeId,
|
|
254
|
+
hasSelection: () => Boolean(this.#modeId),
|
|
255
|
+
resolve: () => this.#modes.find((mode) => mode.id === this.#modeId) ?? {
|
|
256
|
+
id: this.#modeId,
|
|
257
|
+
name: this.#modeId
|
|
258
|
+
},
|
|
259
|
+
set: ({ modeId }) => {
|
|
260
|
+
this.#modeId = modeId;
|
|
261
|
+
},
|
|
262
|
+
switch: async ({ modeId }) => {
|
|
263
|
+
await this.#remote.switchMode(modeId);
|
|
264
|
+
this.#modeId = modeId;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
model = {
|
|
268
|
+
get: () => this.#modelId,
|
|
269
|
+
hasSelection: () => Boolean(this.#modelId),
|
|
270
|
+
displayName: () => this.#modelId,
|
|
271
|
+
set: ({ modelId }) => {
|
|
272
|
+
this.#modelId = modelId;
|
|
273
|
+
},
|
|
274
|
+
switch: async (modelId, options = {}) => {
|
|
275
|
+
await this.#remote.switchModel(modelId, options);
|
|
276
|
+
this.#modelId = modelId;
|
|
277
|
+
},
|
|
278
|
+
syncFromPersisted: async () => {}
|
|
279
|
+
};
|
|
280
|
+
state = {
|
|
281
|
+
get: () => this.#state,
|
|
282
|
+
set: async (updates) => {
|
|
283
|
+
this.#state = {
|
|
284
|
+
...this.#state,
|
|
285
|
+
...updates
|
|
286
|
+
};
|
|
287
|
+
await this.#remote.setState(updates);
|
|
288
|
+
},
|
|
289
|
+
setIf: async (updates, shouldApply) => {
|
|
290
|
+
if (!shouldApply()) return false;
|
|
291
|
+
await this.state.set(updates);
|
|
292
|
+
return true;
|
|
293
|
+
},
|
|
294
|
+
update: async (updater) => {
|
|
295
|
+
const { updates, result } = await updater(this.#state);
|
|
296
|
+
if (updates) await this.state.set(updates);
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
displayState = {
|
|
301
|
+
get: () => this.#displayState,
|
|
302
|
+
clearModifiedFiles: () => {
|
|
303
|
+
this.#displayState = {
|
|
304
|
+
...this.#displayState,
|
|
305
|
+
modifiedFiles: /* @__PURE__ */ new Map()
|
|
306
|
+
};
|
|
307
|
+
},
|
|
308
|
+
restoreTasks: () => {},
|
|
309
|
+
clearPendingSuspensions: () => {},
|
|
310
|
+
deletePendingSuspension: () => {},
|
|
311
|
+
clearPendingApprovals: () => {},
|
|
312
|
+
resetThread: () => {},
|
|
313
|
+
apply: () => {}
|
|
314
|
+
};
|
|
315
|
+
run = {
|
|
316
|
+
isRunning: () => this.#running,
|
|
317
|
+
getRunId: () => null,
|
|
318
|
+
getTraceId: () => null
|
|
319
|
+
};
|
|
320
|
+
stream = {
|
|
321
|
+
isActive: () => this.#running,
|
|
322
|
+
isOpen: () => this.#running
|
|
323
|
+
};
|
|
324
|
+
suspensions = {
|
|
325
|
+
hasPending: () => this.#displayState.pendingSuspensions.size > 0,
|
|
326
|
+
get: ({ toolCallId }) => this.#displayState.pendingSuspensions.get(toolCallId),
|
|
327
|
+
has: ({ toolCallId }) => this.#displayState.pendingSuspensions.has(toolCallId),
|
|
328
|
+
delete: () => {}
|
|
329
|
+
};
|
|
330
|
+
permissions = {
|
|
331
|
+
getRules: () => this.#permissionRules,
|
|
332
|
+
setForCategory: async ({ category, policy }) => {
|
|
333
|
+
await this.#remote.setPermissionForCategory(category, policy);
|
|
334
|
+
await this.#refreshPermissions();
|
|
335
|
+
},
|
|
336
|
+
setForTool: async ({ toolName, policy }) => {
|
|
337
|
+
await this.#remote.setPermissionForTool(toolName, policy);
|
|
338
|
+
await this.#refreshPermissions();
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
getCurrentRunId() {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
getTags() {
|
|
345
|
+
return {};
|
|
346
|
+
}
|
|
347
|
+
getTokenUsage() {
|
|
348
|
+
return this.#displayState.tokenUsage;
|
|
349
|
+
}
|
|
350
|
+
getGrants() {
|
|
351
|
+
return {
|
|
352
|
+
categories: [],
|
|
353
|
+
tools: []
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
async sendMessage({ content, files }) {
|
|
357
|
+
await this.#remote.sendMessage({
|
|
358
|
+
content,
|
|
359
|
+
files
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
async queueMessage({ content }) {
|
|
363
|
+
await this.#remote.followUp(content);
|
|
364
|
+
}
|
|
365
|
+
sendSignal(input) {
|
|
366
|
+
const content = "content" in input ? input.content : void 0;
|
|
367
|
+
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => typeof part === "string" ? part : part?.text ?? "").join("") : JSON.stringify(content);
|
|
368
|
+
const files = "files" in input ? input.files : void 0;
|
|
369
|
+
const message = files?.length ? {
|
|
370
|
+
content: text,
|
|
371
|
+
files
|
|
372
|
+
} : text;
|
|
373
|
+
const id = randomUUID();
|
|
374
|
+
this.#pendingUserIds.push(id);
|
|
375
|
+
const sent = this.#running ? this.#remote.followUp(message) : this.#remote.sendMessage(message);
|
|
376
|
+
sent.catch(() => {
|
|
377
|
+
this.#pendingUserIds = this.#pendingUserIds.filter((pending) => pending !== id);
|
|
378
|
+
});
|
|
379
|
+
const accepted = sent.then(() => ({ accepted: true }));
|
|
380
|
+
accepted.catch(() => {});
|
|
381
|
+
return {
|
|
382
|
+
id,
|
|
383
|
+
type: "user",
|
|
384
|
+
accepted,
|
|
385
|
+
persisted: sent
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
async steer({ content }) {
|
|
389
|
+
await this.#remote.steer(content);
|
|
390
|
+
}
|
|
391
|
+
async followUp({ content }) {
|
|
392
|
+
await this.#remote.followUp(content);
|
|
393
|
+
}
|
|
394
|
+
respondToToolApproval({ decision, toolCallId }) {
|
|
395
|
+
(async () => {
|
|
396
|
+
const ack = await this.#remote.approveTool(toolCallId, decision !== "decline", { alwaysAllowCategory: decision === "always_allow_category" });
|
|
397
|
+
if (ack && "accepted" in ack && ack.accepted === false) this.#emitError(`Tool approval was not applied: ${ack.reason ?? "rejected"}`);
|
|
398
|
+
})().catch((error) => this.#emitError(`Tool approval failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
399
|
+
return { accepted: true };
|
|
400
|
+
}
|
|
401
|
+
async respondToToolSuspension({ resumeData, toolCallId }) {
|
|
402
|
+
const id = toolCallId ?? this.#displayState.pendingSuspensions.keys().next().value;
|
|
403
|
+
if (!id) throw new Error("No pending tool suspension to respond to");
|
|
404
|
+
await this.#remote.respondToToolSuspension(id, resumeData);
|
|
405
|
+
}
|
|
406
|
+
claimToolResponse() {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
releaseToolResponse() {}
|
|
410
|
+
abort() {
|
|
411
|
+
this.#remote.abort().catch((error) => this.#emitError(`Abort failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
412
|
+
}
|
|
413
|
+
abortRun() {
|
|
414
|
+
this.abort();
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
//#endregion
|
|
418
|
+
export { RemoteSession };
|
|
419
|
+
|
|
420
|
+
//# sourceMappingURL=remote-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-session.js","names":["#remote","#sessionId","#listeners","#userIdMap","#resourceId","#hydrate","#refreshPermissions","#receive","#unsubscribe","#modes","#threadId","#modeId","#modelId","#running","#state","#displayState","#permissionRules","#pendingUserIds","#adoptLocalUserId","#emitError"],"sources":["../../src/factory/remote-session.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { AgentControllerEvent as ClientEvent, AgentControllerModeInfo } from '@mastra/client-js';\nimport type {\n AgentControllerDisplayState,\n AgentControllerEvent,\n AgentControllerEventListener,\n AgentControllerRequestStateUpdater,\n AgentControllerThread,\n PermissionRules,\n Session,\n} from '@mastra/core/agent-controller';\nimport { defaultDisplayState } from '@mastra/core/agent-controller';\n\nimport type { FactoryAddress } from './client.js';\nimport { createFactoryClient, FACTORY_CONTROLLER_ID } from './client.js';\n\ntype RemoteHandle = ReturnType<ReturnType<ReturnType<typeof createFactoryClient>['getAgentController']>['session']>;\ntype RemoteMode = AgentControllerModeInfo;\ntype Files = Array<{ data: string; mediaType: string; filename?: string }>;\n\nconst MAP_FIELDS = [\n 'activeTools',\n 'toolInputBuffers',\n 'pendingSuspensions',\n 'pendingApprovals',\n 'activeSubagents',\n 'modifiedFiles',\n] as const;\n\n/** The wire flattens the display state's Maps to objects; restore them. */\nfunction hydrateDisplayState(wire: Record<string, unknown>): AgentControllerDisplayState {\n const state: Record<string, unknown> = { ...defaultDisplayState(), ...wire };\n for (const field of MAP_FIELDS) {\n const value = wire[field];\n state[field] = value instanceof Map ? value : new Map(Object.entries((value as object | undefined) ?? {}));\n }\n return state as unknown as AgentControllerDisplayState;\n}\n\n/** The wire flattens errors to `{ name, message }`; restore them. */\nfunction hydrateEvent(event: ClientEvent): AgentControllerEvent {\n const out: Record<string, unknown> = { ...event };\n if (out.displayState && typeof out.displayState === 'object') {\n out.displayState = hydrateDisplayState(out.displayState as Record<string, unknown>);\n }\n const error = out.error as { name?: string; message?: string } | undefined;\n if (error && typeof error === 'object' && !(error instanceof Error)) {\n out.error = Object.assign(new Error(error.message ?? 'Remote error'), { name: error.name ?? 'Error' });\n }\n return out as unknown as AgentControllerEvent;\n}\n\nfunction toThread(\n thread: { id: string; title?: string; resourceId?: string; createdAt?: unknown; updatedAt?: unknown },\n resourceId: string,\n): AgentControllerThread {\n const date = (value: unknown) => (value ? new Date(value as string) : new Date());\n return {\n id: thread.id,\n resourceId: thread.resourceId ?? resourceId,\n title: thread.title ?? '',\n createdAt: date(thread.createdAt),\n updatedAt: date(thread.updatedAt),\n } as AgentControllerThread;\n}\n\n/**\n * Stands in for an in-process `Session` while the agent, tools, and storage\n * run inside a Factory. Reads are served from state mirrored off the session's\n * event stream; every action is forwarded to the Factory over HTTP.\n *\n * Only the surface the TUI drives is implemented. Anything else resolves to a\n * no-op so local-only features (thread locks, grants) degrade instead of crash.\n */\nexport class RemoteSession {\n readonly #remote: RemoteHandle;\n readonly #sessionId: string;\n /** Whose threads and memory the session works on; the Factory reports it when the session is created. */\n #resourceId: string;\n readonly #listeners = new Set<AgentControllerEventListener>();\n #unsubscribe: (() => void) | undefined;\n #threadId: string | null = null;\n #modeId = '';\n #modelId = '';\n #running = false;\n #state: Record<string, unknown> = {};\n #displayState: AgentControllerDisplayState = defaultDisplayState();\n #modes: RemoteMode[] = [];\n #permissionRules: PermissionRules = { categories: {}, tools: {} };\n #pendingUserIds: string[] = [];\n readonly #userIdMap = new Map<string, string>();\n\n constructor(address: FactoryAddress, sessionId: string, remote?: RemoteHandle) {\n this.#sessionId = sessionId;\n this.#resourceId = sessionId;\n this.#remote = remote ?? createFactoryClient(address).getAgentController(FACTORY_CONTROLLER_ID).session(sessionId);\n }\n\n /** Create/resume the remote session, hydrate the mirror, and open the event stream. */\n async connect(): Promise<void> {\n const created = await this.#remote.create();\n if (created?.resourceId) this.#resourceId = created.resourceId;\n await Promise.all([this.#hydrate(), this.#refreshPermissions()]);\n const subscription = await this.#remote.subscribe({\n reconnect: true,\n onEvent: event => this.#receive(hydrateEvent(event)),\n // The server does not replay missed events; resync after a drop.\n onReconnect: () => void this.#hydrate().catch(() => {}),\n });\n this.#unsubscribe = subscription.unsubscribe;\n }\n\n /** Modes come from the controller; used to resolve the current mode's name and color. */\n setModes(modes: RemoteMode[]): void {\n this.#modes = modes;\n }\n\n disconnect(): void {\n this.#unsubscribe?.();\n this.#unsubscribe = undefined;\n }\n\n async #hydrate(): Promise<void> {\n const snapshot = await this.#remote.state();\n this.#threadId = snapshot.threadId ?? null;\n this.#modeId = snapshot.modeId;\n this.#modelId = snapshot.modelId;\n this.#running = Boolean(snapshot.running);\n // Settings (yolo, thinkingLevel, notifications, smartEditing) are session\n // state; the TUI reads them through state.get().\n if (snapshot.settings) this.#state = { ...this.#state, ...snapshot.settings };\n this.#displayState = {\n ...this.#displayState,\n ...(snapshot.tasks ? { tasks: snapshot.tasks } : {}),\n ...(snapshot.tokenUsage ? { tokenUsage: snapshot.tokenUsage } : {}),\n } as AgentControllerDisplayState;\n }\n\n async #refreshPermissions(): Promise<void> {\n const rules = await this.#remote.getPermissions();\n this.#permissionRules = {\n categories: (rules.categories ?? {}) as PermissionRules['categories'],\n tools: rules.tools ?? {},\n };\n }\n\n /** Surface a failed remote command in the transcript instead of dropping it. */\n #emitError(message: string): void {\n this.#receive({ type: 'error', error: new Error(message) } as AgentControllerEvent);\n }\n\n /**\n * The TUI renders a user message as soon as it is sent, keyed by the id\n * `sendSignal` returned. The Factory echoes it back under its own id, so map\n * that echo onto the local id to keep it from rendering twice.\n */\n #adoptLocalUserId(e: Record<string, any>): void {\n const message = e.message as\n | { id?: string; role?: string; content?: { metadata?: { signal?: { type?: string } } } }\n | undefined;\n const isUser =\n message?.role === 'user' || (message?.role === 'signal' && message.content?.metadata?.signal?.type === 'user');\n if (!message?.id || !isUser) return;\n let localId = this.#userIdMap.get(message.id);\n if (!localId && e.type === 'message_start') {\n localId = this.#pendingUserIds.shift();\n if (localId) this.#userIdMap.set(message.id, localId);\n }\n if (localId) e.message = { ...message, id: localId };\n }\n\n #receive(event: AgentControllerEvent): void {\n const e = event as AgentControllerEvent & Record<string, any>;\n if (e.type === 'message_start' || e.type === 'message_update' || e.type === 'message_end') {\n this.#adoptLocalUserId(e);\n }\n switch (e.type) {\n case 'mode_changed':\n this.#modeId = e.modeId;\n break;\n case 'model_changed':\n this.#modelId = e.modelId;\n break;\n case 'thread_changed':\n this.#threadId = e.threadId;\n break;\n case 'thread_created':\n this.#threadId = e.thread.id;\n break;\n case 'state_changed':\n this.#state = { ...this.#state, ...e.state };\n break;\n case 'display_state_changed':\n this.#displayState = e.displayState;\n break;\n case 'agent_start':\n this.#running = true;\n break;\n case 'agent_end':\n this.#running = false;\n break;\n }\n for (const listener of this.#listeners) {\n void Promise.resolve(listener(event)).catch(() => {});\n }\n }\n\n subscribe(listener: AgentControllerEventListener): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n onBeforeAgentEnd(): () => void {\n return () => {};\n }\n\n readonly identity = {\n getResourceId: () => this.#resourceId,\n getDefaultResourceId: () => this.#resourceId,\n getId: () => this.#sessionId,\n getOwnerId: () => this.#sessionId,\n setResourceId: () => {},\n };\n\n readonly thread = (() => {\n const remote = () => this.#remote;\n const resourceId = () => this.#resourceId;\n return {\n getId: () => this.#threadId,\n isSet: () => this.#threadId !== null,\n requireId: () => {\n if (!this.#threadId) throw new Error('No active thread');\n return this.#threadId;\n },\n ensureId: async () => {\n if (!this.#threadId) await this.thread.create();\n return this.#threadId!;\n },\n list: async () => (await remote().listThreads()).map(t => toThread(t, resourceId())),\n getById: async ({ threadId }: { threadId: string }) =>\n (await this.thread.list()).find(t => t.id === threadId) ?? null,\n create: async ({ title }: { title?: string } = {}) => {\n const created = await remote().createThread(title);\n const thread = toThread(created as any, resourceId());\n this.#threadId = thread.id;\n return thread;\n },\n switch: async ({ threadId }: { threadId: string }) => {\n await remote().switchThread(threadId);\n this.#threadId = threadId;\n },\n clone: async ({ sourceThreadId, title }: { sourceThreadId?: string; title?: string } = {}) => {\n const created = await remote().cloneThread({ sourceThreadId, title });\n const thread = toThread(created as any, resourceId());\n this.#threadId = thread.id;\n return thread;\n },\n rename: async ({ title }: { title: string }) => {\n if (this.#threadId) await remote().renameThread(this.#threadId, title);\n },\n delete: async ({ threadId }: { threadId: string }) => {\n await remote().deleteThread(threadId);\n if (this.#threadId === threadId) this.#threadId = null;\n },\n listMessages: ({ threadId, limit }: { threadId: string; limit?: number }) =>\n remote().listMessages(threadId, limit),\n listActiveMessages: async ({ limit }: { limit?: number } = {}) =>\n this.#threadId ? remote().listMessages(this.#threadId, limit) : [],\n firstUserMessage: async () => null,\n firstUserMessages: async () => new Map(),\n // Thread settings live in the Factory; local overrides are not persisted.\n getSetting: async () => undefined,\n getSettingOn: async () => undefined,\n setSetting: async () => {},\n setSettingOn: async () => {},\n deleteSetting: async () => {},\n cleanupSubscription: () => {},\n detachFromCurrent: () => {},\n clear: () => {\n this.#threadId = null;\n },\n clearAndReleaseLock: async () => {},\n cloneToCurrentResource: async ({ sourceThreadId }: { sourceThreadId: string }) =>\n this.thread.clone({ sourceThreadId }),\n loadMetadata: async () => {},\n };\n })();\n\n readonly mode = {\n get: () => this.#modeId,\n hasSelection: () => Boolean(this.#modeId),\n resolve: () => this.#modes.find(mode => mode.id === this.#modeId) ?? { id: this.#modeId, name: this.#modeId },\n set: ({ modeId }: { modeId: string }) => {\n this.#modeId = modeId;\n },\n switch: async ({ modeId }: { modeId: string }) => {\n await this.#remote.switchMode(modeId);\n this.#modeId = modeId;\n },\n };\n\n readonly model = {\n get: () => this.#modelId,\n hasSelection: () => Boolean(this.#modelId),\n displayName: () => this.#modelId,\n set: ({ modelId }: { modelId: string }) => {\n this.#modelId = modelId;\n },\n switch: async (modelId: string, options: { thinkingLevel?: any } = {}) => {\n await this.#remote.switchModel(modelId, options);\n this.#modelId = modelId;\n },\n syncFromPersisted: async () => {},\n };\n\n readonly state = {\n get: () => this.#state,\n set: async (updates: Record<string, unknown>) => {\n this.#state = { ...this.#state, ...updates };\n await this.#remote.setState(updates);\n },\n setIf: async (updates: Record<string, unknown>, shouldApply: () => boolean) => {\n if (!shouldApply()) return false;\n await this.state.set(updates);\n return true;\n },\n update: async <TResult>(updater: AgentControllerRequestStateUpdater<any, TResult>): Promise<TResult> => {\n const { updates, result } = await updater(this.#state);\n if (updates) await this.state.set(updates);\n return result;\n },\n };\n\n readonly displayState = {\n get: () => this.#displayState,\n clearModifiedFiles: () => {\n this.#displayState = { ...this.#displayState, modifiedFiles: new Map() };\n },\n restoreTasks: () => {},\n clearPendingSuspensions: () => {},\n deletePendingSuspension: () => {},\n clearPendingApprovals: () => {},\n resetThread: () => {},\n apply: () => {},\n };\n\n readonly run = {\n isRunning: () => this.#running,\n getRunId: () => null,\n getTraceId: () => null,\n };\n\n readonly stream = { isActive: () => this.#running, isOpen: () => this.#running };\n\n readonly suspensions = {\n hasPending: () => this.#displayState.pendingSuspensions.size > 0,\n get: ({ toolCallId }: { toolCallId: string }) => this.#displayState.pendingSuspensions.get(toolCallId),\n has: ({ toolCallId }: { toolCallId: string }) => this.#displayState.pendingSuspensions.has(toolCallId),\n delete: () => {},\n };\n\n readonly permissions = {\n getRules: () => this.#permissionRules,\n setForCategory: async ({ category, policy }: { category: any; policy: any }) => {\n await this.#remote.setPermissionForCategory(category, policy);\n await this.#refreshPermissions();\n },\n setForTool: async ({ toolName, policy }: { toolName: string; policy: any }) => {\n await this.#remote.setPermissionForTool(toolName, policy);\n await this.#refreshPermissions();\n },\n };\n\n getCurrentRunId(): string | null {\n return null;\n }\n\n getTags(): Record<string, string> {\n return {};\n }\n\n getTokenUsage() {\n return this.#displayState.tokenUsage;\n }\n\n getGrants() {\n return { categories: [], tools: [] };\n }\n\n async sendMessage({ content, files }: { content: string; files?: Files }) {\n await this.#remote.sendMessage({ content, files });\n }\n\n async queueMessage({ content }: { content: string }) {\n await this.#remote.followUp(content);\n }\n\n sendSignal(input: Parameters<Session['sendSignal']>[0]) {\n const content: unknown = 'content' in input ? input.content : undefined;\n const text =\n typeof content === 'string'\n ? content\n : Array.isArray(content)\n ? content.map(part => (typeof part === 'string' ? part : ((part as { text?: string })?.text ?? ''))).join('')\n : JSON.stringify(content);\n const files = 'files' in input ? (input.files as Files | undefined) : undefined;\n const message = files?.length ? { content: text, files } : text;\n const id = randomUUID();\n this.#pendingUserIds.push(id);\n const sent = this.#running ? this.#remote.followUp(message) : this.#remote.sendMessage(message);\n sent.catch(() => {\n this.#pendingUserIds = this.#pendingUserIds.filter(pending => pending !== id);\n });\n const accepted = sent.then(() => ({ accepted: true as const }));\n accepted.catch(() => {});\n return { id, type: 'user' as const, accepted, persisted: sent };\n }\n\n async steer({ content }: { content: string }) {\n await this.#remote.steer(content);\n }\n\n async followUp({ content }: { content: string }) {\n await this.#remote.followUp(content);\n }\n\n respondToToolApproval({\n decision,\n toolCallId,\n }: {\n decision: 'approve' | 'decline' | 'always_allow_category';\n toolCallId: string;\n }) {\n void (async () => {\n const ack = await this.#remote.approveTool(toolCallId, decision !== 'decline', {\n alwaysAllowCategory: decision === 'always_allow_category',\n });\n if (ack && 'accepted' in ack && ack.accepted === false) {\n this.#emitError(`Tool approval was not applied: ${(ack as { reason?: string }).reason ?? 'rejected'}`);\n }\n })().catch(error =>\n this.#emitError(`Tool approval failed: ${error instanceof Error ? error.message : String(error)}`),\n );\n return { accepted: true as const };\n }\n\n async respondToToolSuspension({ resumeData, toolCallId }: { resumeData: unknown; toolCallId?: string }) {\n const id = toolCallId ?? this.#displayState.pendingSuspensions.keys().next().value;\n if (!id) throw new Error('No pending tool suspension to respond to');\n await this.#remote.respondToToolSuspension(\n id,\n resumeData as Parameters<RemoteHandle['respondToToolSuspension']>[1],\n );\n }\n\n claimToolResponse(): boolean {\n return true;\n }\n\n releaseToolResponse(): void {}\n\n abort(): void {\n void this.#remote\n .abort()\n .catch(error => this.#emitError(`Abort failed: ${error instanceof Error ? error.message : String(error)}`));\n }\n\n abortRun(): void {\n this.abort();\n }\n}\n"],"mappings":"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"}
|
package/dist/headless/cli.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Writable } from 'node:stream';
|
|
1
2
|
import type { PermissionMode, RunMode, ThinkingLevel } from './types.js';
|
|
2
3
|
/** Consolidated output mode (replaces the old `--format` + `--output-format`). */
|
|
3
4
|
export type OutputMode = 'human' | 'json' | 'jsonl';
|
|
@@ -35,6 +36,12 @@ export declare function hasHeadlessFlag(argv: string[]): boolean;
|
|
|
35
36
|
*/
|
|
36
37
|
export declare function parseHeadlessArgs(argv: string[]): HeadlessArgs;
|
|
37
38
|
export declare function printHeadlessUsage(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Wait until everything written to `stream` has been handed to the OS, since
|
|
41
|
+
* `process.exit()` drops writes a pipe has not accepted yet. Resolves `false`
|
|
42
|
+
* if the stream fails or closes first; never rejects or hangs on a gone reader.
|
|
43
|
+
*/
|
|
44
|
+
export declare function flushWritable(stream: Writable): Promise<boolean>;
|
|
38
45
|
/**
|
|
39
46
|
* Headless CLI entry point: parse arguments, read stdin, initialize MastraCode,
|
|
40
47
|
* run via `runMC`, render output, and exit with the mapped code.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/headless/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/headless/cli.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAmB5C,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzE,kFAAkF;AAClF,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAKD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAcvD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CAsC9D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CA+BzC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAShE;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACzD,OAAO,CAAC,KAAK,CAAC,CAwLhB"}
|
package/dist/headless/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { loadSettings, resolveModelDefaults, saveSettings } from "../onboarding/
|
|
|
3
3
|
import { releaseAllThreadLocks } from "../utils/thread-lock.js";
|
|
4
4
|
import { permissionModeToPolicy } from "./policy.js";
|
|
5
5
|
import { runMC } from "./run-mc.js";
|
|
6
|
-
import { createHumanFormatState, formatHuman, formatJsonl, renderJsonResult } from "./format.js";
|
|
6
|
+
import { createHumanFormatState, formatHuman, formatJsonl, isJsonlEvent, renderJsonResult } from "./format.js";
|
|
7
7
|
import { activateModePackInSettings, resolveModePackArg, resolvePackSelection } from "../onboarding/pack-activation.js";
|
|
8
8
|
import { createProcessMemoryDiagnosticsFromEnvironment, startConfiguredProcessMemoryDiagnostics, stopProcessMemoryDiagnosticsWithTimeout } from "../process-memory-diagnostics.js";
|
|
9
9
|
import { setupDebugLogging } from "../utils/debug-log.js";
|
|
@@ -104,6 +104,20 @@ Examples:
|
|
|
104
104
|
`);
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
|
+
* Wait until everything written to `stream` has been handed to the OS, since
|
|
108
|
+
* `process.exit()` drops writes a pipe has not accepted yet. Resolves `false`
|
|
109
|
+
* if the stream fails or closes first; never rejects or hangs on a gone reader.
|
|
110
|
+
*/
|
|
111
|
+
function flushWritable(stream) {
|
|
112
|
+
if (stream.destroyed || stream.errored) return Promise.resolve(false);
|
|
113
|
+
if (stream.writableLength === 0) return Promise.resolve(true);
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
stream.once("error", () => resolve(false));
|
|
116
|
+
stream.once("close", () => resolve(false));
|
|
117
|
+
stream.write("", (error) => resolve(!error));
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
107
121
|
* Headless CLI entry point: parse arguments, read stdin, initialize MastraCode,
|
|
108
122
|
* run via `runMC`, render output, and exit with the mapped code.
|
|
109
123
|
*/
|
|
@@ -195,7 +209,7 @@ async function runMCCli(predrainedInput, options) {
|
|
|
195
209
|
const out = formatHuman(event, humanState);
|
|
196
210
|
if (out.stdout) process.stdout.write(out.stdout);
|
|
197
211
|
if (out.stderr) process.stderr.write(out.stderr);
|
|
198
|
-
} else if (args.output === "jsonl") process.stdout.write(JSON.stringify(formatJsonl(event)) + "\n");
|
|
212
|
+
} else if (args.output === "jsonl" && isJsonlEvent(event)) process.stdout.write(JSON.stringify(formatJsonl(event)) + "\n");
|
|
199
213
|
const result = await run.result;
|
|
200
214
|
exitCode = result.exitCode;
|
|
201
215
|
if (args.output === "json") process.stdout.write(renderJsonResult(result));
|
|
@@ -231,9 +245,11 @@ async function runMCCli(predrainedInput, options) {
|
|
|
231
245
|
process.stderr.write(`Warning: ${warning}\n`);
|
|
232
246
|
});
|
|
233
247
|
}
|
|
248
|
+
const [stdoutFlushed] = await Promise.all([flushWritable(process.stdout), flushWritable(process.stderr)]);
|
|
249
|
+
if (!stdoutFlushed && exitCode === 0) exitCode = 1;
|
|
234
250
|
process.exit(exitCode);
|
|
235
251
|
}
|
|
236
252
|
//#endregion
|
|
237
|
-
export { hasHeadlessFlag, parseHeadlessArgs, printHeadlessUsage, runMCCli };
|
|
253
|
+
export { flushWritable, hasHeadlessFlag, parseHeadlessArgs, printHeadlessUsage, runMCCli };
|
|
238
254
|
|
|
239
255
|
//# sourceMappingURL=cli.js.map
|
package/dist/headless/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../../src/headless/cli.ts"],"sourcesContent":["/**\n * CLI adapter for headless MastraCode runs.\n *\n * This is the only headless layer that touches the process: it parses argv,\n * reads stdin, bootstraps MastraCode via `createMastraCode`, drives `runMC`,\n * renders events/results to stdout/stderr through the pure formatters, maps the\n * result to an exit code, and owns teardown + `process.exit`.\n */\nimport { existsSync } from 'node:fs';\nimport { parseArgs } from 'node:util';\n\nimport { createMastraCode } from '../index.js';\nimport { activateModePackInSettings, resolveModePackArg, resolvePackSelection } from '../onboarding/pack-activation.js';\nimport { listBuiltinModePacks } from '../onboarding/packs.js';\nimport { loadSettings, resolveModelDefaults, saveSettings } from '../onboarding/settings.js';\nimport {\n createProcessMemoryDiagnosticsFromEnvironment,\n startConfiguredProcessMemoryDiagnostics,\n stopProcessMemoryDiagnosticsWithTimeout,\n} from '../process-memory-diagnostics.js';\nimport { setupDebugLogging } from '../utils/debug-log.js';\nimport { releaseAllThreadLocks } from '../utils/thread-lock.js';\n\nimport { buildParseArgsOptions, FLAGS, renderFlagUsage } from './flags.js';\nimport { createHumanFormatState, formatHuman, formatJsonl, renderJsonResult } from './format.js';\nimport { permissionModeToPolicy } from './policy.js';\nimport { runMC } from './run-mc.js';\nimport type { PermissionMode, RunMode, ThinkingLevel } from './types.js';\n\n/** Consolidated output mode (replaces the old `--format` + `--output-format`). */\nexport type OutputMode = 'human' | 'json' | 'jsonl';\n\nexport interface HeadlessArgs {\n prompt?: string;\n /** Timeout in seconds (CLI surface); converted to ms before `runMC`. */\n timeout?: number;\n output: OutputMode;\n continue_: boolean;\n model?: string;\n pack?: string;\n mode?: RunMode;\n thinkingLevel?: ThinkingLevel;\n settings?: string;\n thread?: string;\n title?: string;\n cloneThread: boolean;\n resourceId?: string;\n /** Max agentic turns before the run aborts with exit code 1. */\n maxTurns?: number;\n /** Named permission mode resolving to a built-in policy. Defaults to `auto`. */\n permissionMode?: PermissionMode;\n}\n\nconst parseArgsOptions = buildParseArgsOptions();\nconst STOP_NOTIFICATION_DISPATCH_TIMEOUT_MS = 2_000;\n\n/**\n * Returns true if `argv` selects headless mode. This must agree with what\n * {@link parseHeadlessArgs} (and `runMCCli`) accept as a prompt: `--prompt`/`-p`\n * or a bare positional prompt (e.g. `mastracode \"Fix the bug\"`). Note that a\n * prompt piped via stdin without a flag is handled separately by the caller.\n */\nexport function hasHeadlessFlag(argv: string[]): boolean {\n if (argv.some(a => a === '--prompt' || a === '-p')) return true;\n try {\n const { values, positionals } = parseArgs({\n args: argv.slice(2),\n options: parseArgsOptions,\n strict: false,\n allowPositionals: true,\n });\n // A positional prompt only counts when not asking for help.\n return positionals.length > 0 && !values.help;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse CLI arguments for headless mode. The flag table in `flags.ts` is the\n * single source of truth: each flag carries its own coercion/validation, so this\n * function just walks {@link FLAGS} and assembles the typed {@link HeadlessArgs}.\n */\nexport function parseHeadlessArgs(argv: string[]): HeadlessArgs {\n const { values, positionals } = parseArgs({\n args: argv.slice(2),\n options: parseArgsOptions,\n strict: false,\n allowPositionals: true,\n });\n\n // Seed defaults; per-flag values below override these.\n const args: HeadlessArgs = {\n output: 'human',\n continue_: false,\n cloneThread: false,\n };\n const sink = args as unknown as Record<string, unknown>;\n\n for (const flag of FLAGS) {\n if (!flag.field) continue; // e.g. --help, handled by the caller\n const raw = values[flag.key];\n if (raw === undefined) continue;\n\n if (flag.type === 'boolean') {\n sink[flag.field] = Boolean(raw);\n } else if (typeof raw === 'string') {\n sink[flag.field] = flag.coerce ? flag.coerce(raw) : raw;\n }\n }\n\n // A bare positional acts as the prompt when --prompt/-p is absent.\n if (args.prompt === undefined && positionals[0] !== undefined) {\n args.prompt = positionals[0];\n }\n\n if (args.continue_ && args.thread) {\n throw new Error('--continue and --thread cannot be used together');\n }\n\n return args;\n}\n\nexport function printHeadlessUsage(): void {\n process.stdout.write(`\nUsage: mastracode --prompt <text> [options]\n\nHeadless (non-interactive) mode options:\n${renderFlagUsage()}\n\nThread behavior:\n By default, a new thread is created for each run.\n Use --continue to resume the most recent thread, or --thread to target a specific one.\n Use --clone-thread to branch off a copy before running.\n\nSettings file:\n Uses the same settings.json as the interactive TUI. Pass --settings to use\n a custom settings file (e.g., settings-ci.json for CI). All model, pack,\n subagent, and OM configuration is resolved from settings at startup.\n\nExit codes:\n 0 Agent completed successfully\n 1 Error, aborted, or max turns reached\n 2 Timeout\n\nExamples:\n mastracode --prompt \"Fix the bug in auth.ts\"\n mastracode --prompt \"Add tests\" --timeout 300 --output json\n mastracode --prompt \"Refactor\" --output jsonl\n mastracode --prompt \"Review this PR\" --permission-mode deny --max-turns 10\n mastracode --settings ./settings-ci.json --prompt \"Run tests\"\n mastracode -c --prompt \"Continue where you left off\"\n echo \"Summarize the repo\" | mastracode --prompt -\n`);\n}\n\n/**\n * Headless CLI entry point: parse arguments, read stdin, initialize MastraCode,\n * run via `runMC`, render output, and exit with the mapped code.\n */\nexport async function runMCCli(\n predrainedInput?: string | null,\n options?: { coAuthor?: { name?: string; email?: string } },\n): Promise<never> {\n if (process.argv.includes('--help') || process.argv.includes('-h')) {\n printHeadlessUsage();\n process.exit(0);\n }\n\n let args: HeadlessArgs;\n try {\n args = parseHeadlessArgs(process.argv);\n } catch (e) {\n process.stderr.write(`Error: ${(e as Error).message}\\n`);\n process.exit(1);\n }\n\n let prompt = args.prompt;\n if (predrainedInput !== undefined) {\n prompt = predrainedInput ?? '';\n } else if (prompt === '-' || (!prompt && !process.stdin.isTTY)) {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n prompt = Buffer.concat(chunks).toString('utf-8').trim();\n }\n\n if (!prompt) {\n printHeadlessUsage();\n process.stderr.write('Error: --prompt is required (or pipe via stdin)\\n');\n process.exit(1);\n }\n\n if (args.settings && !existsSync(args.settings)) {\n process.stderr.write(`Error: Settings file not found: ${args.settings}\\n`);\n process.exit(1);\n }\n\n // Resolve --pack up front: an unknown pack fails fast with the valid list,\n // before MastraCode boots. The selection itself is resolved inside runMC\n // after thread selection, because a resumed thread restores its persisted\n // mode — the mode that decides whether the pack has a model for this run.\n // Activation (which mirrors /models → Activate and becomes the global\n // default pack in settings) is saved via onApplied only once the pack has\n // actually been applied to the session — a rejected pack must not change\n // the persisted default on its way out.\n const settings = loadSettings(args.settings);\n let packId: string | undefined;\n if (args.pack !== undefined) {\n const resolved = resolveModePackArg(settings, args.pack);\n if ('error' in resolved) {\n process.stderr.write(`Error: ${resolved.error}\\n`);\n process.exit(1);\n }\n packId = resolved.packId;\n }\n\n const diagnosticsSetup = createProcessMemoryDiagnosticsFromEnvironment(process.env);\n const processMemoryDiagnostics = await startConfiguredProcessMemoryDiagnostics(diagnosticsSetup, warning => {\n process.stderr.write(`Warning: ${warning}\\n`);\n });\n\n let boot: Awaited<ReturnType<typeof createMastraCode>> | undefined;\n // Default to a non-zero exit so an unexpected throw before the run resolves\n // still surfaces as a failure to the caller / CI.\n let exitCode = 1;\n try {\n boot = await createMastraCode({ settingsPath: args.settings, coAuthor: options?.coAuthor });\n const { controller, session, mcpManager } = boot;\n\n const effectiveDefaults = resolveModelDefaults(settings, listBuiltinModePacks());\n\n if (mcpManager?.hasServers()) {\n try {\n await mcpManager.initInBackground();\n } catch (err) {\n process.stderr.write(`Warning: MCP server initialization failed: ${(err as Error).message ?? err}\\n`);\n }\n }\n\n setupDebugLogging();\n\n const humanState = createHumanFormatState();\n const run = runMC({\n controller,\n session,\n prompt,\n model: args.model,\n mode: args.mode,\n modeDefaults: effectiveDefaults,\n ...(packId\n ? {\n pack: {\n id: packId,\n resolve: (modeId: string) => resolvePackSelection(settings, packId, modeId),\n onApplied: () => {\n activateModePackInSettings(settings, packId);\n saveSettings(settings, args.settings);\n },\n },\n }\n : {}),\n thinkingLevel: args.thinkingLevel,\n thread: { id: args.thread, continueLatest: args.continue_, clone: args.cloneThread },\n resourceId: args.resourceId,\n title: args.title,\n timeoutMs: args.timeout ? args.timeout * 1000 : undefined,\n maxTurns: args.maxTurns,\n policy: args.permissionMode ? permissionModeToPolicy(args.permissionMode) : undefined,\n });\n\n // Stream live events for human + jsonl modes. (json mode prints only the final object.)\n for await (const event of run) {\n if (args.output === 'human') {\n const out = formatHuman(event, humanState);\n if (out.stdout) process.stdout.write(out.stdout);\n if (out.stderr) process.stderr.write(out.stderr);\n } else if (args.output === 'jsonl') {\n process.stdout.write(JSON.stringify(formatJsonl(event)) + '\\n');\n }\n }\n\n const result = await run.result;\n exitCode = result.exitCode;\n\n if (args.output === 'json') {\n process.stdout.write(renderJsonResult(result));\n } else if (args.output === 'jsonl') {\n process.stdout.write(JSON.stringify({ type: 'result', ...result }) + '\\n');\n }\n\n if (result.status === 'timeout') {\n process.stderr.write(`\\nTimeout elapsed. Aborted.\\n`);\n } else if (result.error && args.output === 'human') {\n process.stderr.write(`Error: ${result.error.message}\\n`);\n }\n } catch (err) {\n process.stderr.write(`Error: ${(err as Error).message ?? err}\\n`);\n exitCode = 1;\n } finally {\n // --- Teardown (always runs, even on a thrown error) ---\n releaseAllThreadLocks();\n if (boot) {\n // Stop plugin-contributed signal providers (and the plugin reload listener)\n // before quiescing workers: a provider that keeps polling past this point\n // could dispatch into a controller that is shutting down. Release thread\n // claims first so a restarted process need not wait out this teardown.\n try {\n boot.releaseThreadClaims();\n } catch {\n // Best-effort — the process is exiting.\n }\n try {\n boot.stopPluginSignalProviders();\n } catch {\n // Best-effort — the process is exiting.\n }\n const { controller, mcpManager } = boot;\n // Release notification dispatch leases before the pubsub that holds them\n // closes. Headless has no shutdown deadline, so don't let a stalled\n // in-flight dispatch hold up exit.\n await Promise.race([\n boot.stopNotificationDispatch?.().catch(() => {}),\n new Promise<void>(resolve => setTimeout(resolve, STOP_NOTIFICATION_DISPATCH_TIMEOUT_MS).unref()),\n ]);\n await Promise.allSettled([\n mcpManager?.disconnect(),\n controller.getMastra()?.stopWorkers(),\n controller.stopIntervals(),\n ]);\n // The signals pubsub is Mastra's event bus, so close it after the workers\n // stop. Call close() on the object; a detached method loses `this` and\n // rejects silently, leaving socket files behind.\n const signalsPubSub = boot.signalsPubSub as { close?: () => Promise<void> | void } | undefined;\n await Promise.allSettled([signalsPubSub?.close?.()]);\n }\n await stopProcessMemoryDiagnosticsWithTimeout(processMemoryDiagnostics, warning => {\n process.stderr.write(`Warning: ${warning}\\n`);\n });\n }\n\n process.exit(exitCode);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../../src/headless/cli.ts"],"sourcesContent":["/**\n * CLI adapter for headless MastraCode runs.\n *\n * This is the only headless layer that touches the process: it parses argv,\n * reads stdin, bootstraps MastraCode via `createMastraCode`, drives `runMC`,\n * renders events/results to stdout/stderr through the pure formatters, maps the\n * result to an exit code, and owns teardown + `process.exit`.\n */\nimport { existsSync } from 'node:fs';\nimport type { Writable } from 'node:stream';\nimport { parseArgs } from 'node:util';\n\nimport { createMastraCode } from '../index.js';\nimport { activateModePackInSettings, resolveModePackArg, resolvePackSelection } from '../onboarding/pack-activation.js';\nimport { listBuiltinModePacks } from '../onboarding/packs.js';\nimport { loadSettings, resolveModelDefaults, saveSettings } from '../onboarding/settings.js';\nimport {\n createProcessMemoryDiagnosticsFromEnvironment,\n startConfiguredProcessMemoryDiagnostics,\n stopProcessMemoryDiagnosticsWithTimeout,\n} from '../process-memory-diagnostics.js';\nimport { setupDebugLogging } from '../utils/debug-log.js';\nimport { releaseAllThreadLocks } from '../utils/thread-lock.js';\n\nimport { buildParseArgsOptions, FLAGS, renderFlagUsage } from './flags.js';\nimport { createHumanFormatState, formatHuman, formatJsonl, isJsonlEvent, renderJsonResult } from './format.js';\nimport { permissionModeToPolicy } from './policy.js';\nimport { runMC } from './run-mc.js';\nimport type { PermissionMode, RunMode, ThinkingLevel } from './types.js';\n\n/** Consolidated output mode (replaces the old `--format` + `--output-format`). */\nexport type OutputMode = 'human' | 'json' | 'jsonl';\n\nexport interface HeadlessArgs {\n prompt?: string;\n /** Timeout in seconds (CLI surface); converted to ms before `runMC`. */\n timeout?: number;\n output: OutputMode;\n continue_: boolean;\n model?: string;\n pack?: string;\n mode?: RunMode;\n thinkingLevel?: ThinkingLevel;\n settings?: string;\n thread?: string;\n title?: string;\n cloneThread: boolean;\n resourceId?: string;\n /** Max agentic turns before the run aborts with exit code 1. */\n maxTurns?: number;\n /** Named permission mode resolving to a built-in policy. Defaults to `auto`. */\n permissionMode?: PermissionMode;\n}\n\nconst parseArgsOptions = buildParseArgsOptions();\nconst STOP_NOTIFICATION_DISPATCH_TIMEOUT_MS = 2_000;\n\n/**\n * Returns true if `argv` selects headless mode. This must agree with what\n * {@link parseHeadlessArgs} (and `runMCCli`) accept as a prompt: `--prompt`/`-p`\n * or a bare positional prompt (e.g. `mastracode \"Fix the bug\"`). Note that a\n * prompt piped via stdin without a flag is handled separately by the caller.\n */\nexport function hasHeadlessFlag(argv: string[]): boolean {\n if (argv.some(a => a === '--prompt' || a === '-p')) return true;\n try {\n const { values, positionals } = parseArgs({\n args: argv.slice(2),\n options: parseArgsOptions,\n strict: false,\n allowPositionals: true,\n });\n // A positional prompt only counts when not asking for help.\n return positionals.length > 0 && !values.help;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse CLI arguments for headless mode. The flag table in `flags.ts` is the\n * single source of truth: each flag carries its own coercion/validation, so this\n * function just walks {@link FLAGS} and assembles the typed {@link HeadlessArgs}.\n */\nexport function parseHeadlessArgs(argv: string[]): HeadlessArgs {\n const { values, positionals } = parseArgs({\n args: argv.slice(2),\n options: parseArgsOptions,\n strict: false,\n allowPositionals: true,\n });\n\n // Seed defaults; per-flag values below override these.\n const args: HeadlessArgs = {\n output: 'human',\n continue_: false,\n cloneThread: false,\n };\n const sink = args as unknown as Record<string, unknown>;\n\n for (const flag of FLAGS) {\n if (!flag.field) continue; // e.g. --help, handled by the caller\n const raw = values[flag.key];\n if (raw === undefined) continue;\n\n if (flag.type === 'boolean') {\n sink[flag.field] = Boolean(raw);\n } else if (typeof raw === 'string') {\n sink[flag.field] = flag.coerce ? flag.coerce(raw) : raw;\n }\n }\n\n // A bare positional acts as the prompt when --prompt/-p is absent.\n if (args.prompt === undefined && positionals[0] !== undefined) {\n args.prompt = positionals[0];\n }\n\n if (args.continue_ && args.thread) {\n throw new Error('--continue and --thread cannot be used together');\n }\n\n return args;\n}\n\nexport function printHeadlessUsage(): void {\n process.stdout.write(`\nUsage: mastracode --prompt <text> [options]\n\nHeadless (non-interactive) mode options:\n${renderFlagUsage()}\n\nThread behavior:\n By default, a new thread is created for each run.\n Use --continue to resume the most recent thread, or --thread to target a specific one.\n Use --clone-thread to branch off a copy before running.\n\nSettings file:\n Uses the same settings.json as the interactive TUI. Pass --settings to use\n a custom settings file (e.g., settings-ci.json for CI). All model, pack,\n subagent, and OM configuration is resolved from settings at startup.\n\nExit codes:\n 0 Agent completed successfully\n 1 Error, aborted, or max turns reached\n 2 Timeout\n\nExamples:\n mastracode --prompt \"Fix the bug in auth.ts\"\n mastracode --prompt \"Add tests\" --timeout 300 --output json\n mastracode --prompt \"Refactor\" --output jsonl\n mastracode --prompt \"Review this PR\" --permission-mode deny --max-turns 10\n mastracode --settings ./settings-ci.json --prompt \"Run tests\"\n mastracode -c --prompt \"Continue where you left off\"\n echo \"Summarize the repo\" | mastracode --prompt -\n`);\n}\n\n/**\n * Wait until everything written to `stream` has been handed to the OS, since\n * `process.exit()` drops writes a pipe has not accepted yet. Resolves `false`\n * if the stream fails or closes first; never rejects or hangs on a gone reader.\n */\nexport function flushWritable(stream: Writable): Promise<boolean> {\n if (stream.destroyed || stream.errored) return Promise.resolve(false);\n if (stream.writableLength === 0) return Promise.resolve(true);\n return new Promise(resolve => {\n // Left attached: a stream can emit `error` after failing pending callbacks.\n stream.once('error', () => resolve(false));\n stream.once('close', () => resolve(false));\n stream.write('', error => resolve(!error));\n });\n}\n\n/**\n * Headless CLI entry point: parse arguments, read stdin, initialize MastraCode,\n * run via `runMC`, render output, and exit with the mapped code.\n */\nexport async function runMCCli(\n predrainedInput?: string | null,\n options?: { coAuthor?: { name?: string; email?: string } },\n): Promise<never> {\n if (process.argv.includes('--help') || process.argv.includes('-h')) {\n printHeadlessUsage();\n process.exit(0);\n }\n\n let args: HeadlessArgs;\n try {\n args = parseHeadlessArgs(process.argv);\n } catch (e) {\n process.stderr.write(`Error: ${(e as Error).message}\\n`);\n process.exit(1);\n }\n\n let prompt = args.prompt;\n if (predrainedInput !== undefined) {\n prompt = predrainedInput ?? '';\n } else if (prompt === '-' || (!prompt && !process.stdin.isTTY)) {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n prompt = Buffer.concat(chunks).toString('utf-8').trim();\n }\n\n if (!prompt) {\n printHeadlessUsage();\n process.stderr.write('Error: --prompt is required (or pipe via stdin)\\n');\n process.exit(1);\n }\n\n if (args.settings && !existsSync(args.settings)) {\n process.stderr.write(`Error: Settings file not found: ${args.settings}\\n`);\n process.exit(1);\n }\n\n // Resolve --pack up front: an unknown pack fails fast with the valid list,\n // before MastraCode boots. The selection itself is resolved inside runMC\n // after thread selection, because a resumed thread restores its persisted\n // mode — the mode that decides whether the pack has a model for this run.\n // Activation (which mirrors /models → Activate and becomes the global\n // default pack in settings) is saved via onApplied only once the pack has\n // actually been applied to the session — a rejected pack must not change\n // the persisted default on its way out.\n const settings = loadSettings(args.settings);\n let packId: string | undefined;\n if (args.pack !== undefined) {\n const resolved = resolveModePackArg(settings, args.pack);\n if ('error' in resolved) {\n process.stderr.write(`Error: ${resolved.error}\\n`);\n process.exit(1);\n }\n packId = resolved.packId;\n }\n\n const diagnosticsSetup = createProcessMemoryDiagnosticsFromEnvironment(process.env);\n const processMemoryDiagnostics = await startConfiguredProcessMemoryDiagnostics(diagnosticsSetup, warning => {\n process.stderr.write(`Warning: ${warning}\\n`);\n });\n\n let boot: Awaited<ReturnType<typeof createMastraCode>> | undefined;\n // Default to a non-zero exit so an unexpected throw before the run resolves\n // still surfaces as a failure to the caller / CI.\n let exitCode = 1;\n try {\n boot = await createMastraCode({ settingsPath: args.settings, coAuthor: options?.coAuthor });\n const { controller, session, mcpManager } = boot;\n\n const effectiveDefaults = resolveModelDefaults(settings, listBuiltinModePacks());\n\n if (mcpManager?.hasServers()) {\n try {\n await mcpManager.initInBackground();\n } catch (err) {\n process.stderr.write(`Warning: MCP server initialization failed: ${(err as Error).message ?? err}\\n`);\n }\n }\n\n setupDebugLogging();\n\n const humanState = createHumanFormatState();\n const run = runMC({\n controller,\n session,\n prompt,\n model: args.model,\n mode: args.mode,\n modeDefaults: effectiveDefaults,\n ...(packId\n ? {\n pack: {\n id: packId,\n resolve: (modeId: string) => resolvePackSelection(settings, packId, modeId),\n onApplied: () => {\n activateModePackInSettings(settings, packId);\n saveSettings(settings, args.settings);\n },\n },\n }\n : {}),\n thinkingLevel: args.thinkingLevel,\n thread: { id: args.thread, continueLatest: args.continue_, clone: args.cloneThread },\n resourceId: args.resourceId,\n title: args.title,\n timeoutMs: args.timeout ? args.timeout * 1000 : undefined,\n maxTurns: args.maxTurns,\n policy: args.permissionMode ? permissionModeToPolicy(args.permissionMode) : undefined,\n });\n\n // Stream live events for human + jsonl modes. (json mode prints only the final object.)\n for await (const event of run) {\n if (args.output === 'human') {\n const out = formatHuman(event, humanState);\n if (out.stdout) process.stdout.write(out.stdout);\n if (out.stderr) process.stderr.write(out.stderr);\n } else if (args.output === 'jsonl' && isJsonlEvent(event)) {\n process.stdout.write(JSON.stringify(formatJsonl(event)) + '\\n');\n }\n }\n\n const result = await run.result;\n exitCode = result.exitCode;\n\n if (args.output === 'json') {\n process.stdout.write(renderJsonResult(result));\n } else if (args.output === 'jsonl') {\n process.stdout.write(JSON.stringify({ type: 'result', ...result }) + '\\n');\n }\n\n if (result.status === 'timeout') {\n process.stderr.write(`\\nTimeout elapsed. Aborted.\\n`);\n } else if (result.error && args.output === 'human') {\n process.stderr.write(`Error: ${result.error.message}\\n`);\n }\n } catch (err) {\n process.stderr.write(`Error: ${(err as Error).message ?? err}\\n`);\n exitCode = 1;\n } finally {\n // --- Teardown (always runs, even on a thrown error) ---\n releaseAllThreadLocks();\n if (boot) {\n // Stop plugin-contributed signal providers (and the plugin reload listener)\n // before quiescing workers: a provider that keeps polling past this point\n // could dispatch into a controller that is shutting down. Release thread\n // claims first so a restarted process need not wait out this teardown.\n try {\n boot.releaseThreadClaims();\n } catch {\n // Best-effort — the process is exiting.\n }\n try {\n boot.stopPluginSignalProviders();\n } catch {\n // Best-effort — the process is exiting.\n }\n const { controller, mcpManager } = boot;\n // Release notification dispatch leases before the pubsub that holds them\n // closes. Headless has no shutdown deadline, so don't let a stalled\n // in-flight dispatch hold up exit.\n await Promise.race([\n boot.stopNotificationDispatch?.().catch(() => {}),\n new Promise<void>(resolve => setTimeout(resolve, STOP_NOTIFICATION_DISPATCH_TIMEOUT_MS).unref()),\n ]);\n await Promise.allSettled([\n mcpManager?.disconnect(),\n controller.getMastra()?.stopWorkers(),\n controller.stopIntervals(),\n ]);\n // The signals pubsub is Mastra's event bus, so close it after the workers\n // stop. Call close() on the object; a detached method loses `this` and\n // rejects silently, leaving socket files behind.\n const signalsPubSub = boot.signalsPubSub as { close?: () => Promise<void> | void } | undefined;\n await Promise.allSettled([signalsPubSub?.close?.()]);\n }\n await stopProcessMemoryDiagnosticsWithTimeout(processMemoryDiagnostics, warning => {\n process.stderr.write(`Warning: ${warning}\\n`);\n });\n }\n\n // Let piped output (the final result line in particular) reach the reader\n // before exiting. Output that never arrived must not count as success.\n const [stdoutFlushed] = await Promise.all([flushWritable(process.stdout), flushWritable(process.stderr)]);\n if (!stdoutFlushed && exitCode === 0) exitCode = 1;\n process.exit(exitCode);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsDA,MAAM,mBAAmB,sBAAsB;AAC/C,MAAM,wCAAwC;;;;;;;AAQ9C,SAAgB,gBAAgB,MAAyB;CACvD,IAAI,KAAK,MAAK,MAAK,MAAM,cAAc,MAAM,IAAI,GAAG,OAAO;CAC3D,IAAI;EACF,MAAM,EAAE,QAAQ,gBAAgB,UAAU;GACxC,MAAM,KAAK,MAAM,CAAC;GAClB,SAAS;GACT,QAAQ;GACR,kBAAkB;EACpB,CAAC;EAED,OAAO,YAAY,SAAS,KAAK,CAAC,OAAO;CAC3C,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,kBAAkB,MAA8B;CAC9D,MAAM,EAAE,QAAQ,gBAAgB,UAAU;EACxC,MAAM,KAAK,MAAM,CAAC;EAClB,SAAS;EACT,QAAQ;EACR,kBAAkB;CACpB,CAAC;CAGD,MAAM,OAAqB;EACzB,QAAQ;EACR,WAAW;EACX,aAAa;CACf;CACA,MAAM,OAAO;CAEb,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,KAAK,OAAO;EACjB,MAAM,MAAM,OAAO,KAAK;EACxB,IAAI,QAAQ,KAAA,GAAW;EAEvB,IAAI,KAAK,SAAS,WAChB,KAAK,KAAK,SAAS,QAAQ,GAAG;OACzB,IAAI,OAAO,QAAQ,UACxB,KAAK,KAAK,SAAS,KAAK,SAAS,KAAK,OAAO,GAAG,IAAI;CAExD;CAGA,IAAI,KAAK,WAAW,KAAA,KAAa,YAAY,OAAO,KAAA,GAClD,KAAK,SAAS,YAAY;CAG5B,IAAI,KAAK,aAAa,KAAK,QACzB,MAAM,IAAI,MAAM,iDAAiD;CAGnE,OAAO;AACT;AAEA,SAAgB,qBAA2B;CACzC,QAAQ,OAAO,MAAM;;;;EAIrB,gBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;CAyBnB;AACD;;;;;;AAOA,SAAgB,cAAc,QAAoC;CAChE,IAAI,OAAO,aAAa,OAAO,SAAS,OAAO,QAAQ,QAAQ,KAAK;CACpE,IAAI,OAAO,mBAAmB,GAAG,OAAO,QAAQ,QAAQ,IAAI;CAC5D,OAAO,IAAI,SAAQ,YAAW;EAE5B,OAAO,KAAK,eAAe,QAAQ,KAAK,CAAC;EACzC,OAAO,KAAK,eAAe,QAAQ,KAAK,CAAC;EACzC,OAAO,MAAM,KAAI,UAAS,QAAQ,CAAC,KAAK,CAAC;CAC3C,CAAC;AACH;;;;;AAMA,eAAsB,SACpB,iBACA,SACgB;CAChB,IAAI,QAAQ,KAAK,SAAS,QAAQ,KAAK,QAAQ,KAAK,SAAS,IAAI,GAAG;EAClE,mBAAmB;EACnB,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI;CACJ,IAAI;EACF,OAAO,kBAAkB,QAAQ,IAAI;CACvC,SAAS,GAAG;EACV,QAAQ,OAAO,MAAM,UAAW,EAAY,QAAQ,GAAG;EACvD,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,SAAS,KAAK;CAClB,IAAI,oBAAoB,KAAA,GACtB,SAAS,mBAAmB;MACvB,IAAI,WAAW,OAAQ,CAAC,UAAU,CAAC,QAAQ,MAAM,OAAQ;EAC9D,MAAM,SAAmB,CAAC;EAC1B,WAAW,MAAM,SAAS,QAAQ,OAChC,OAAO,KAAK,KAAe;EAE7B,SAAS,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,OAAO,CAAC,CAAC,KAAK;CACxD;CAEA,IAAI,CAAC,QAAQ;EACX,mBAAmB;EACnB,QAAQ,OAAO,MAAM,mDAAmD;EACxE,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,KAAK,YAAY,CAAC,WAAW,KAAK,QAAQ,GAAG;EAC/C,QAAQ,OAAO,MAAM,mCAAmC,KAAK,SAAS,GAAG;EACzE,QAAQ,KAAK,CAAC;CAChB;CAUA,MAAM,WAAW,aAAa,KAAK,QAAQ;CAC3C,IAAI;CACJ,IAAI,KAAK,SAAS,KAAA,GAAW;EAC3B,MAAM,WAAW,mBAAmB,UAAU,KAAK,IAAI;EACvD,IAAI,WAAW,UAAU;GACvB,QAAQ,OAAO,MAAM,UAAU,SAAS,MAAM,GAAG;GACjD,QAAQ,KAAK,CAAC;EAChB;EACA,SAAS,SAAS;CACpB;CAGA,MAAM,2BAA2B,MAAM,wCADd,8CAA8C,QAAQ,GACe,IAAG,YAAW;EAC1G,QAAQ,OAAO,MAAM,YAAY,QAAQ,GAAG;CAC9C,CAAC;CAED,IAAI;CAGJ,IAAI,WAAW;CACf,IAAI;EACF,OAAO,MAAM,iBAAiB;GAAE,cAAc,KAAK;GAAU,UAAU,SAAS;EAAS,CAAC;EAC1F,MAAM,EAAE,YAAY,SAAS,eAAe;EAE5C,MAAM,oBAAoB,qBAAqB,UAAU,qBAAqB,CAAC;EAE/E,IAAI,YAAY,WAAW,GACzB,IAAI;GACF,MAAM,WAAW,iBAAiB;EACpC,SAAS,KAAK;GACZ,QAAQ,OAAO,MAAM,8CAA+C,IAAc,WAAW,IAAI,GAAG;EACtG;EAGF,kBAAkB;EAElB,MAAM,aAAa,uBAAuB;EAC1C,MAAM,MAAM,MAAM;GAChB;GACA;GACA;GACA,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,cAAc;GACd,GAAI,SACA,EACE,MAAM;IACJ,IAAI;IACJ,UAAU,WAAmB,qBAAqB,UAAU,QAAQ,MAAM;IAC1E,iBAAiB;KACf,2BAA2B,UAAU,MAAM;KAC3C,aAAa,UAAU,KAAK,QAAQ;IACtC;GACF,EACF,IACA,CAAC;GACL,eAAe,KAAK;GACpB,QAAQ;IAAE,IAAI,KAAK;IAAQ,gBAAgB,KAAK;IAAW,OAAO,KAAK;GAAY;GACnF,YAAY,KAAK;GACjB,OAAO,KAAK;GACZ,WAAW,KAAK,UAAU,KAAK,UAAU,MAAO,KAAA;GAChD,UAAU,KAAK;GACf,QAAQ,KAAK,iBAAiB,uBAAuB,KAAK,cAAc,IAAI,KAAA;EAC9E,CAAC;EAGD,WAAW,MAAM,SAAS,KACxB,IAAI,KAAK,WAAW,SAAS;GAC3B,MAAM,MAAM,YAAY,OAAO,UAAU;GACzC,IAAI,IAAI,QAAQ,QAAQ,OAAO,MAAM,IAAI,MAAM;GAC/C,IAAI,IAAI,QAAQ,QAAQ,OAAO,MAAM,IAAI,MAAM;EACjD,OAAO,IAAI,KAAK,WAAW,WAAW,aAAa,KAAK,GACtD,QAAQ,OAAO,MAAM,KAAK,UAAU,YAAY,KAAK,CAAC,IAAI,IAAI;EAIlE,MAAM,SAAS,MAAM,IAAI;EACzB,WAAW,OAAO;EAElB,IAAI,KAAK,WAAW,QAClB,QAAQ,OAAO,MAAM,iBAAiB,MAAM,CAAC;OACxC,IAAI,KAAK,WAAW,SACzB,QAAQ,OAAO,MAAM,KAAK,UAAU;GAAE,MAAM;GAAU,GAAG;EAAO,CAAC,IAAI,IAAI;EAG3E,IAAI,OAAO,WAAW,WACpB,QAAQ,OAAO,MAAM,+BAA+B;OAC/C,IAAI,OAAO,SAAS,KAAK,WAAW,SACzC,QAAQ,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,GAAG;CAE3D,SAAS,KAAK;EACZ,QAAQ,OAAO,MAAM,UAAW,IAAc,WAAW,IAAI,GAAG;EAChE,WAAW;CACb,UAAU;EAER,sBAAsB;EACtB,IAAI,MAAM;GAKR,IAAI;IACF,KAAK,oBAAoB;GAC3B,QAAQ,CAER;GACA,IAAI;IACF,KAAK,0BAA0B;GACjC,QAAQ,CAER;GACA,MAAM,EAAE,YAAY,eAAe;GAInC,MAAM,QAAQ,KAAK,CACjB,KAAK,2BAA2B,CAAC,CAAC,YAAY,CAAC,CAAC,GAChD,IAAI,SAAc,YAAW,WAAW,SAAS,qCAAqC,CAAC,CAAC,MAAM,CAAC,CACjG,CAAC;GACD,MAAM,QAAQ,WAAW;IACvB,YAAY,WAAW;IACvB,WAAW,UAAU,CAAC,EAAE,YAAY;IACpC,WAAW,cAAc;GAC3B,CAAC;GAID,MAAM,gBAAgB,KAAK;GAC3B,MAAM,QAAQ,WAAW,CAAC,eAAe,QAAQ,CAAC,CAAC;EACrD;EACA,MAAM,wCAAwC,2BAA0B,YAAW;GACjF,QAAQ,OAAO,MAAM,YAAY,QAAQ,GAAG;EAC9C,CAAC;CACH;CAIA,MAAM,CAAC,iBAAiB,MAAM,QAAQ,IAAI,CAAC,cAAc,QAAQ,MAAM,GAAG,cAAc,QAAQ,MAAM,CAAC,CAAC;CACxG,IAAI,CAAC,iBAAiB,aAAa,GAAG,WAAW;CACjD,QAAQ,KAAK,QAAQ;AACvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/headless/flags.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,aAAa,qCAAsC,CAAC;AA6BjE;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC3B,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/headless/flags.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,aAAa,qCAAsC,CAAC;AA6BjE;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC3B,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,KAAK,EAAE,QAAQ,EA+I3B,CAAC;AAEF,4DAA4D;AAC5D,wBAAgB,qBAAqB;UACG,QAAQ,GAAG,SAAS;YAAU,MAAM;cAAY,OAAO;GAM9F;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,IAAI,MAAM,CAmBxC"}
|