@cydm/happy-elves 0.1.0-beta.248 → 0.1.0-beta.254
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/apps/cli/dist/commands/session.js +2 -1
- package/apps/cli/dist/commands/turn.js +7 -2
- package/apps/cli/package.json +1 -1
- package/apps/daemon/dist/gateway/runtime.js +2 -0
- package/apps/daemon/dist/relay/admission.d.ts +2 -0
- package/apps/daemon/dist/relay/connection.js +5 -1
- package/apps/daemon/dist/relay/register.js +2 -0
- package/apps/daemon/dist/runtime/external-provider.js +56 -14
- package/apps/daemon/dist/session/events.js +10 -0
- package/apps/daemon/dist/session/lifecycle.js +7 -0
- package/apps/daemon/dist/session/metadata.d.ts +1 -1
- package/apps/daemon/dist/session/metadata.js +2 -0
- package/apps/daemon/dist/session/prompt.d.ts +2 -1
- package/apps/daemon/dist/session/prompt.js +122 -2
- package/apps/daemon/dist/types.d.ts +3 -0
- package/apps/daemon/package.json +1 -1
- package/apps/relay/dist/controller-handlers.js +27 -0
- package/apps/relay/dist/http-routes.js +2 -0
- package/apps/relay/dist/http-schemas.d.ts +2 -0
- package/apps/relay/dist/http-schemas.js +2 -0
- package/apps/relay/dist/machine-handlers.js +14 -2
- package/apps/relay/dist/relay-context.js +45 -1
- package/package.json +1 -1
- package/packages/agent-sdk/dist/index.d.ts +39 -0
- package/packages/agent-sdk/dist/index.js +12 -0
- package/packages/client/dist/client.js +8 -0
- package/packages/client/dist/types.d.ts +2 -0
- package/packages/runtime/dist/index.d.ts +30 -0
- package/packages/runtime-cli/dist/claude-turn.d.ts +1 -0
- package/packages/runtime-cli/dist/claude-turn.js +1 -0
- package/packages/runtime-cli/dist/codex-app-server.d.ts +5 -2
- package/packages/runtime-cli/dist/codex-app-server.js +25 -16
- package/packages/runtime-cli/dist/codex-protocol.d.ts +2 -2
- package/packages/runtime-cli/dist/codex-protocol.js +4 -4
- package/packages/runtime-cli/dist/index.js +79 -2
- package/packages/runtime-cli/dist/session-store.d.ts +1 -0
- package/packages/runtime-cli/dist/session-store.js +9 -8
- package/packages/shared/dist/index.d.ts +1 -0
- package/packages/shared/dist/index.js +1 -0
- package/packages/shared/dist/protocol-schemas.d.ts +14 -0
- package/packages/shared/dist/protocol-schemas.js +7 -0
- package/packages/shared/dist/protocol-types.d.ts +32 -0
- package/packages/shared/dist/protocol.d.ts +26 -0
- package/packages/shared/dist/protocol.js +29 -0
|
@@ -353,8 +353,9 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
353
353
|
const requestedRuntimeName = typeof flags.name === "string" ? flags.name : undefined;
|
|
354
354
|
const name = "main";
|
|
355
355
|
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
356
|
+
const model = typeof flags.model === "string" && flags.model.trim() ? flags.model.trim() : undefined;
|
|
356
357
|
const client = await makeClient();
|
|
357
|
-
const result = await client.createSession({ machineId, agent, cwd, name, runtimeName: requestedRuntimeName, permissionMode });
|
|
358
|
+
const result = await client.createSession({ machineId, agent, cwd, name, runtimeName: requestedRuntimeName, permissionMode, model });
|
|
358
359
|
const session = result.sessionId ? await client.getSession(result.sessionId) : undefined;
|
|
359
360
|
const projectedSession = session
|
|
360
361
|
? wantsVerbose(flags)
|
|
@@ -201,6 +201,8 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
201
201
|
: "";
|
|
202
202
|
if (!continuation.trim())
|
|
203
203
|
throw new CliError("Missing --text or --text-file", "MISSING_ARGUMENT");
|
|
204
|
+
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
205
|
+
const model = typeof flags.model === "string" && flags.model.trim() ? flags.model.trim() : undefined;
|
|
204
206
|
const sourceSession = await requireProjectedSession(client, sessionId, "turn.recover");
|
|
205
207
|
const sourceMetadata = await client.decodeSessionMetadata(sourceSession);
|
|
206
208
|
const sourceName = sourceMetadata.name ?? sourceSession.name;
|
|
@@ -229,7 +231,8 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
229
231
|
cwd: sourceMetadata.cwd ?? sourceSession.cwd,
|
|
230
232
|
name: recoveryName,
|
|
231
233
|
createdFrom: "turn.recover",
|
|
232
|
-
permissionMode
|
|
234
|
+
permissionMode,
|
|
235
|
+
model,
|
|
233
236
|
});
|
|
234
237
|
if (!created.sessionId)
|
|
235
238
|
throw new CliError("Recovery session create was acknowledged without sessionId", "RELAY_RESPONSE_INVALID");
|
|
@@ -237,7 +240,6 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
237
240
|
promptText = recoveryPromptWithContext(turns, inputTurnId, continuation);
|
|
238
241
|
}
|
|
239
242
|
await client.wait(recoverySessionId, { timeoutMs: 120_000 });
|
|
240
|
-
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
241
243
|
const wait = flags.detach !== true && flags["no-wait"] !== true;
|
|
242
244
|
const memoryMode = parseMemoryModeFlag(flags["memory-mode"] ?? flags.memory) ?? "readonly";
|
|
243
245
|
const recoverySession = await client.getSession(recoverySessionId);
|
|
@@ -250,6 +252,7 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
250
252
|
wait,
|
|
251
253
|
permissionMode,
|
|
252
254
|
memoryMode,
|
|
255
|
+
model,
|
|
253
256
|
...(memoryProject ? { memoryProjectKey: memoryProject } : {}),
|
|
254
257
|
memoryTopN: 3,
|
|
255
258
|
waitTimeoutMs: parseDurationMs(flags.timeout, DEFAULT_COMMAND_TIMEOUT_MS),
|
|
@@ -289,6 +292,7 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
289
292
|
const ackTimeoutMs = 15_000;
|
|
290
293
|
const inactivityTimeoutMs = parseDurationMs(flags["inactivity-timeout"] ?? flags["runtime-timeout"], DEFAULT_COMMAND_TIMEOUT_MS);
|
|
291
294
|
const memoryMode = parseMemoryModeFlag(flags["memory-mode"] ?? flags.memory) ?? "readonly";
|
|
295
|
+
const model = typeof flags.model === "string" && flags.model.trim() ? flags.model.trim() : undefined;
|
|
292
296
|
const session = await client.getSession(sessionId);
|
|
293
297
|
const memoryProject = typeof flags["memory-project"] === "string"
|
|
294
298
|
? flags["memory-project"]
|
|
@@ -302,6 +306,7 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
302
306
|
wait,
|
|
303
307
|
permissionMode,
|
|
304
308
|
memoryMode,
|
|
309
|
+
model,
|
|
305
310
|
...(memoryProject ? { memoryProjectKey: memoryProject } : {}),
|
|
306
311
|
memoryTopN,
|
|
307
312
|
waitTimeoutMs: watchTimeoutMs,
|
package/apps/cli/package.json
CHANGED
|
@@ -294,6 +294,7 @@ async function createGatewayMapping(config, channel, externalConversationId, cre
|
|
|
294
294
|
agent: channel.defaultAgent,
|
|
295
295
|
cwd: channel.targetCwd,
|
|
296
296
|
name: `Gateway ${channel.type}: ${externalConversationId}`,
|
|
297
|
+
...(channel.defaultModel ? { model: channel.defaultModel } : {}),
|
|
297
298
|
encryptedMetadata: await encryptJson(config.accountSecret, `gateway:create:${createRequestId}`, {
|
|
298
299
|
createdFrom: "gateway",
|
|
299
300
|
gateway: {
|
|
@@ -353,6 +354,7 @@ async function admitGatewayPromptNow(config, channel, input) {
|
|
|
353
354
|
text: input.text,
|
|
354
355
|
}),
|
|
355
356
|
permissionMode: "approve-reads",
|
|
357
|
+
...(channel.defaultModel ? { model: channel.defaultModel } : {}),
|
|
356
358
|
});
|
|
357
359
|
const turnId = sent.turnId ?? input.turnId;
|
|
358
360
|
const lastAdmitted = {
|
|
@@ -15,6 +15,7 @@ export declare function admitMachineCreateSession(config: DaemonConfig, input: {
|
|
|
15
15
|
agent: string;
|
|
16
16
|
cwd: string;
|
|
17
17
|
name: string;
|
|
18
|
+
model?: string;
|
|
18
19
|
encryptedMetadata?: EncryptedEnvelope;
|
|
19
20
|
}): Promise<MachineAdmissionAck>;
|
|
20
21
|
export declare function admitMachinePrompt(config: DaemonConfig, input: {
|
|
@@ -24,5 +25,6 @@ export declare function admitMachinePrompt(config: DaemonConfig, input: {
|
|
|
24
25
|
encryptedPrompt: EncryptedEnvelope;
|
|
25
26
|
encryptedMemoryOptions?: EncryptedEnvelope;
|
|
26
27
|
permissionMode?: PermissionMode;
|
|
28
|
+
model?: string;
|
|
27
29
|
timeoutMs?: number;
|
|
28
30
|
}): Promise<MachineAdmissionAck>;
|
|
@@ -7,7 +7,7 @@ import { tickSchedules } from "../schedule/runner.js";
|
|
|
7
7
|
import { handleListDirectory } from "../session/directory.js";
|
|
8
8
|
import { handlePreviewFile } from "../session/file-preview.js";
|
|
9
9
|
import { handleClose, handleCreateSession, handleImportHistoricalSession, handleListHistoricalSessions, handleResume } from "../session/lifecycle.js";
|
|
10
|
-
import { handleCancel, handlePrompt } from "../session/prompt.js";
|
|
10
|
+
import { handleCancel, handleOptionResponse, handlePrompt } from "../session/prompt.js";
|
|
11
11
|
import { handleFork, handleRename, handleRewind } from "../session/primitives.js";
|
|
12
12
|
import { handleReconcileTurn } from "../session/reconcile.js";
|
|
13
13
|
import { parseRelayMessage, relayWsUrl } from "../relay-http.js";
|
|
@@ -87,6 +87,10 @@ async function handleServerMessage(ws, config, message) {
|
|
|
87
87
|
await handleCancel(ws, config, message);
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
|
+
if (message.type === "machine:optionResponse") {
|
|
91
|
+
await handleOptionResponse(ws, config, message);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
90
94
|
if (message.type === "machine:reconcileTurn") {
|
|
91
95
|
await handleReconcileTurn(ws, config, message);
|
|
92
96
|
return;
|
|
@@ -22,6 +22,8 @@ function agentProfileForRegister(agent) {
|
|
|
22
22
|
id: agent.id,
|
|
23
23
|
label: agent.label,
|
|
24
24
|
...(agent.command ? { command: agent.command } : {}),
|
|
25
|
+
...(agent.defaultModel ? { defaultModel: agent.defaultModel } : {}),
|
|
26
|
+
...(agent.models && agent.models.length > 0 ? { models: agent.models } : {}),
|
|
25
27
|
...(agent.historyModel ? { historyModel: agent.historyModel } : {}),
|
|
26
28
|
...(agent.sessionPrimitives ? { sessionPrimitives: runtimeAgentPrimitives(agent.sessionPrimitives) } : {}),
|
|
27
29
|
};
|
|
@@ -226,6 +226,7 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
226
226
|
agentSessionId: handle.sessionId,
|
|
227
227
|
externalProviderId: handle.provider,
|
|
228
228
|
externalHandle: handle,
|
|
229
|
+
...(handle.model ? { model: handle.model } : {}),
|
|
229
230
|
};
|
|
230
231
|
}
|
|
231
232
|
function sdkHandle(handle) {
|
|
@@ -282,20 +283,26 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
282
283
|
return options.fallback.getCapabilities();
|
|
283
284
|
},
|
|
284
285
|
listAgents() {
|
|
285
|
-
const externalAgents = clients.flatMap((client) => client.listAgents()).map((agent) =>
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
286
|
+
const externalAgents = clients.flatMap((client) => client.listAgents()).map((agent) => {
|
|
287
|
+
const defaultModel = normalizedModelId(agent.defaultModel);
|
|
288
|
+
const models = normalizeModelOptions(agent.models);
|
|
289
|
+
return {
|
|
290
|
+
id: agent.id,
|
|
291
|
+
label: agent.label,
|
|
292
|
+
command: agent.command,
|
|
293
|
+
...(defaultModel ? { defaultModel } : {}),
|
|
294
|
+
...(models ? { models } : {}),
|
|
295
|
+
historyModel: normalizeHistoryModel(agent.historyModel),
|
|
296
|
+
sessionPrimitives: agent.capabilities?.sessionPrimitives
|
|
297
|
+
? {
|
|
298
|
+
resume: fromSdkPrimitive(agent.capabilities.sessionPrimitives.resume),
|
|
299
|
+
rewind: fromSdkPrimitive(agent.capabilities.sessionPrimitives.rewind),
|
|
300
|
+
fork: fromSdkPrimitive(agent.capabilities.sessionPrimitives.fork),
|
|
301
|
+
rename: agent.capabilities.sessionPrimitives.rename ? fromSdkPrimitive(agent.capabilities.sessionPrimitives.rename) : "unsupported",
|
|
302
|
+
}
|
|
303
|
+
: undefined,
|
|
304
|
+
};
|
|
305
|
+
});
|
|
299
306
|
const agentsById = new Map();
|
|
300
307
|
for (const agent of externalAgents)
|
|
301
308
|
agentsById.set(agent.id, agent);
|
|
@@ -318,6 +325,7 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
318
325
|
...(input.resumeHandle ? { resumeHandle: sdkHandle(input.resumeHandle) } : {}),
|
|
319
326
|
cwd: input.cwd,
|
|
320
327
|
permissionMode: input.permissionMode,
|
|
328
|
+
model: input.model,
|
|
321
329
|
restartableEmptySession: input.restartableEmptySession,
|
|
322
330
|
});
|
|
323
331
|
return toRuntimeHandle(input.sessionKey, handle, input.cwd);
|
|
@@ -466,6 +474,7 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
466
474
|
mode: input.mode,
|
|
467
475
|
permissionMode: input.permissionMode,
|
|
468
476
|
requestId: input.requestId,
|
|
477
|
+
model: input.model ?? input.handle.model,
|
|
469
478
|
});
|
|
470
479
|
},
|
|
471
480
|
cancel(input) {
|
|
@@ -523,6 +532,31 @@ function emptyPageUnlessCursorStale(error) {
|
|
|
523
532
|
throw error;
|
|
524
533
|
return { sessions: [] };
|
|
525
534
|
}
|
|
535
|
+
function normalizedModelId(value) {
|
|
536
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
537
|
+
}
|
|
538
|
+
function normalizeModelOptions(value) {
|
|
539
|
+
if (!Array.isArray(value))
|
|
540
|
+
return undefined;
|
|
541
|
+
const seen = new Set();
|
|
542
|
+
const models = value.flatMap((item) => {
|
|
543
|
+
if (!isRecord(item))
|
|
544
|
+
return [];
|
|
545
|
+
const id = normalizedModelId(item.id);
|
|
546
|
+
if (!id || seen.has(id))
|
|
547
|
+
return [];
|
|
548
|
+
seen.add(id);
|
|
549
|
+
const label = normalizedModelId(item.label);
|
|
550
|
+
const reason = normalizedModelId(item.reason);
|
|
551
|
+
return [{
|
|
552
|
+
id,
|
|
553
|
+
...(label ? { label } : {}),
|
|
554
|
+
...(item.unavailable === true ? { unavailable: true } : {}),
|
|
555
|
+
...(reason ? { reason } : {}),
|
|
556
|
+
}];
|
|
557
|
+
});
|
|
558
|
+
return models.length > 0 ? models : undefined;
|
|
559
|
+
}
|
|
526
560
|
class ExternalProviderClient {
|
|
527
561
|
config;
|
|
528
562
|
id;
|
|
@@ -653,6 +687,14 @@ class ExternalProviderClient {
|
|
|
653
687
|
cancel: async (input) => {
|
|
654
688
|
await this.request("cancel", { handle: params.handle, reason: input?.reason });
|
|
655
689
|
},
|
|
690
|
+
respondOption: async (input) => {
|
|
691
|
+
await this.request("respondOption", {
|
|
692
|
+
handle: params.handle,
|
|
693
|
+
optionRequestId: input.optionRequestId,
|
|
694
|
+
...(input.selectedOptionId ? { selectedOptionId: input.selectedOptionId } : {}),
|
|
695
|
+
...(input.text ? { text: input.text } : {}),
|
|
696
|
+
});
|
|
697
|
+
},
|
|
656
698
|
closeStream: async (input) => {
|
|
657
699
|
await this.request("cancel", { handle: params.handle, reason: input?.reason ?? "stream closed" });
|
|
658
700
|
},
|
|
@@ -147,6 +147,16 @@ export function runtimeEventToPayload(event) {
|
|
|
147
147
|
title: event.title,
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
|
+
if (event.type === "option_request") {
|
|
151
|
+
return {
|
|
152
|
+
type: "option_request",
|
|
153
|
+
optionRequestId: event.optionRequestId,
|
|
154
|
+
...(event.title ? { title: event.title } : {}),
|
|
155
|
+
message: event.message,
|
|
156
|
+
options: event.options,
|
|
157
|
+
...(event.allowFreeText ? { allowFreeText: true } : {}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
150
160
|
if (event.type === "status") {
|
|
151
161
|
if (typeof event.used === "number" && Number.isFinite(event.used)) {
|
|
152
162
|
return {
|
|
@@ -15,6 +15,7 @@ export async function handleCreateSession(ws, config, command) {
|
|
|
15
15
|
const cwd = command.cwd || process.cwd();
|
|
16
16
|
try {
|
|
17
17
|
const metadata = await commandSessionMetadata(config, command.encryptedMetadata);
|
|
18
|
+
const model = modelFromCommand(command.model ?? metadata?.model);
|
|
18
19
|
const runtime = getRuntime(cwd);
|
|
19
20
|
let handle = await runtime.ensureSession({
|
|
20
21
|
sessionKey: command.sessionId,
|
|
@@ -22,6 +23,7 @@ export async function handleCreateSession(ws, config, command) {
|
|
|
22
23
|
mode: "persistent",
|
|
23
24
|
cwd,
|
|
24
25
|
permissionMode: metadata?.permissionMode,
|
|
26
|
+
model,
|
|
25
27
|
});
|
|
26
28
|
let providerName;
|
|
27
29
|
const requestedRuntimeName = metadata?.requestedRuntimeName;
|
|
@@ -39,6 +41,7 @@ export async function handleCreateSession(ws, config, command) {
|
|
|
39
41
|
agent: command.agent,
|
|
40
42
|
cwd,
|
|
41
43
|
name: providerName ?? command.name,
|
|
44
|
+
...(model ? { model } : {}),
|
|
42
45
|
handle,
|
|
43
46
|
};
|
|
44
47
|
sessions.set(command.sessionId, state);
|
|
@@ -53,6 +56,7 @@ export async function handleCreateSession(ws, config, command) {
|
|
|
53
56
|
agent: command.agent,
|
|
54
57
|
cwd,
|
|
55
58
|
name: state.name,
|
|
59
|
+
model,
|
|
56
60
|
requestedRuntimeName: requestedRuntimeName ? true : undefined,
|
|
57
61
|
runtime: {
|
|
58
62
|
acpxRecordId: handle.acpxRecordId,
|
|
@@ -78,6 +82,9 @@ export async function handleCreateSession(ws, config, command) {
|
|
|
78
82
|
await sendCommandResponse(ws, createSessionFailureResponse(command, error));
|
|
79
83
|
}
|
|
80
84
|
}
|
|
85
|
+
function modelFromCommand(value) {
|
|
86
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
87
|
+
}
|
|
81
88
|
export async function handleImportHistoricalSession(ws, config, command) {
|
|
82
89
|
if (command.type !== "machine:importHistoricalSession")
|
|
83
90
|
return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type EncryptedEnvelope, type MachineCommand } from "../../../../packages/shared/dist/index.js";
|
|
2
2
|
import type { DaemonConfig, SessionFallback, SessionMetadata, SessionState } from "../types.js";
|
|
3
|
-
export declare function encryptedSessionMetadata(config: DaemonConfig, sessionId: string, state: Pick<SessionState, "agent" | "cwd" | "name" | "handle">, extra: Record<string, unknown>): Promise<EncryptedEnvelope>;
|
|
3
|
+
export declare function encryptedSessionMetadata(config: DaemonConfig, sessionId: string, state: Pick<SessionState, "agent" | "cwd" | "name" | "handle" | "model">, extra: Record<string, unknown>): Promise<EncryptedEnvelope>;
|
|
4
4
|
export declare function commandSessionMetadata(config: DaemonConfig, encryptedMetadata?: EncryptedEnvelope): Promise<SessionMetadata | undefined>;
|
|
5
5
|
export declare function sessionFallbackFromCommand(config: DaemonConfig, command: Extract<MachineCommand, {
|
|
6
6
|
type: "machine:prompt" | "machine:resume" | "machine:rewind" | "machine:fork" | "machine:renameSession" | "machine:close";
|
|
@@ -4,6 +4,7 @@ export function encryptedSessionMetadata(config, sessionId, state, extra) {
|
|
|
4
4
|
agent: state.agent,
|
|
5
5
|
cwd: state.cwd,
|
|
6
6
|
name: state.name,
|
|
7
|
+
...(state.model ? { model: state.model } : {}),
|
|
7
8
|
runtime: {
|
|
8
9
|
backend: state.handle.backend,
|
|
9
10
|
runtimeSessionName: state.handle.runtimeSessionName,
|
|
@@ -36,6 +37,7 @@ export async function sessionFallbackFromCommand(config, command) {
|
|
|
36
37
|
agent: metadata.agent ?? "codex",
|
|
37
38
|
cwd: metadata.cwd || process.cwd(),
|
|
38
39
|
name: metadata.name ?? command.sessionId,
|
|
40
|
+
...(typeof metadata.model === "string" && metadata.model.trim() ? { model: metadata.model.trim() } : {}),
|
|
39
41
|
emptyRuntimeSession: metadata.emptyRuntimeSession === true,
|
|
40
42
|
historicalBackfill: historicalBackfillStateFromMetadata(metadata),
|
|
41
43
|
runtime: metadata.runtime,
|
|
@@ -3,8 +3,9 @@ import type { DaemonConfig } from "../types.js";
|
|
|
3
3
|
type PromptCommand = Extract<MachineCommand, {
|
|
4
4
|
type: "machine:prompt";
|
|
5
5
|
}>;
|
|
6
|
-
type PromptExecutionPhase = "decrypt-prompt" | "decrypt-memory-options" | "prepare-memory-context" | "emit-submitted" | "emit-memory-status" | "start-turn" | "attach-turn" | "audit-turn-start" | "stream-turn" | "await-turn-result" | "write-memory" | "refresh-runtime-metadata" | "emit-terminal" | "refresh-authoritative-transcript" | "send-turn-done" | "terminal-hooks";
|
|
6
|
+
type PromptExecutionPhase = "decrypt-prompt" | "prepare-attachments" | "decrypt-memory-options" | "prepare-memory-context" | "emit-submitted" | "emit-memory-status" | "start-turn" | "attach-turn" | "audit-turn-start" | "stream-turn" | "await-turn-result" | "write-memory" | "refresh-runtime-metadata" | "emit-terminal" | "refresh-authoritative-transcript" | "send-turn-done" | "terminal-hooks";
|
|
7
7
|
export declare function handlePrompt(ws: WebSocket, config: DaemonConfig, command: MachineCommand): Promise<void>;
|
|
8
|
+
export declare function handleOptionResponse(ws: WebSocket, config: DaemonConfig, command: MachineCommand): Promise<void>;
|
|
8
9
|
export declare function promptFailureResponse(command: PromptCommand, phase: PromptExecutionPhase, error: unknown): {
|
|
9
10
|
type: "machine:error";
|
|
10
11
|
requestId: string;
|
|
@@ -56,12 +56,15 @@ export async function handlePrompt(ws, config, command) {
|
|
|
56
56
|
try {
|
|
57
57
|
const prompt = await decryptJson(config.accountSecret, command.encryptedPrompt);
|
|
58
58
|
const rawText = prompt.type === "user_prompt" ? prompt.text : JSON.stringify(prompt);
|
|
59
|
+
const promptAttachments = prompt.type === "user_prompt" ? normalizePromptAttachments(prompt.attachments) : [];
|
|
60
|
+
phase = "prepare-attachments";
|
|
61
|
+
const preparedPrompt = preparePromptAttachments(rawText, promptAttachments);
|
|
59
62
|
phase = "decrypt-memory-options";
|
|
60
63
|
const memoryOptions = await decryptMemoryOptions(config, command);
|
|
61
64
|
phase = "prepare-memory-context";
|
|
62
65
|
const memoryContext = memoryOptions
|
|
63
|
-
? await prepareMemoryContext(memoryOptions,
|
|
64
|
-
: { text:
|
|
66
|
+
? await prepareMemoryContext(memoryOptions, preparedPrompt.text)
|
|
67
|
+
: { text: preparedPrompt.text, used: [], options: undefined };
|
|
65
68
|
const text = memoryContext.text;
|
|
66
69
|
phase = "emit-submitted";
|
|
67
70
|
await emitEvent(ws, config, command.sessionId, command.turnId, {
|
|
@@ -95,12 +98,15 @@ export async function handlePrompt(ws, config, command) {
|
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
const previousBackendSessionId = session.handle.backendSessionId;
|
|
101
|
+
const model = modelFromCommand(command.model) ?? session.model ?? session.handle.model;
|
|
98
102
|
phase = "start-turn";
|
|
99
103
|
const turn = getRuntime(session.cwd).startTurn({
|
|
100
104
|
handle: session.handle,
|
|
101
105
|
text,
|
|
102
106
|
mode: "prompt",
|
|
103
107
|
permissionMode: command.permissionMode,
|
|
108
|
+
model,
|
|
109
|
+
attachments: preparedPrompt.runtimeAttachments,
|
|
104
110
|
requestId: command.requestId,
|
|
105
111
|
timeoutMs: command.timeoutMs,
|
|
106
112
|
});
|
|
@@ -120,7 +126,10 @@ export async function handlePrompt(ws, config, command) {
|
|
|
120
126
|
turnId: command.turnId,
|
|
121
127
|
promptChars: text.length,
|
|
122
128
|
rawPromptChars: rawText.length,
|
|
129
|
+
attachmentCount: promptAttachments.length,
|
|
130
|
+
runtimeAttachmentCount: preparedPrompt.runtimeAttachments.length,
|
|
123
131
|
permissionMode: command.permissionMode ?? "approve-reads",
|
|
132
|
+
model,
|
|
124
133
|
memoryMode: memoryOptions?.mode,
|
|
125
134
|
usedMemories: memoryContext.used.map((item) => ({ path: item.path, citation: item.citation })),
|
|
126
135
|
},
|
|
@@ -258,6 +267,53 @@ export async function handlePrompt(ws, config, command) {
|
|
|
258
267
|
releasePromptTurn();
|
|
259
268
|
}
|
|
260
269
|
}
|
|
270
|
+
export async function handleOptionResponse(ws, config, command) {
|
|
271
|
+
if (command.type !== "machine:optionResponse")
|
|
272
|
+
return;
|
|
273
|
+
const activeTurnId = sessionTurns.activeTurnId(command.sessionId);
|
|
274
|
+
const activeTurn = sessionTurns.activeTurn(command.sessionId);
|
|
275
|
+
if (!activeTurn || activeTurnId !== command.turnId) {
|
|
276
|
+
await sendCommandResponse(ws, {
|
|
277
|
+
type: "machine:error",
|
|
278
|
+
requestId: command.requestId,
|
|
279
|
+
sessionId: command.sessionId,
|
|
280
|
+
code: "OPTION_REQUEST_NOT_ACTIVE",
|
|
281
|
+
message: "No matching active turn is waiting for this option response",
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (!activeTurn.respondOption) {
|
|
286
|
+
await sendCommandResponse(ws, {
|
|
287
|
+
type: "machine:error",
|
|
288
|
+
requestId: command.requestId,
|
|
289
|
+
sessionId: command.sessionId,
|
|
290
|
+
code: "OPTION_RESPONSE_UNSUPPORTED",
|
|
291
|
+
message: "This runtime does not support Happy Elves option responses",
|
|
292
|
+
});
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
await activeTurn.respondOption({
|
|
296
|
+
optionRequestId: command.optionRequestId,
|
|
297
|
+
...(command.selectedOptionId ? { selectedOptionId: command.selectedOptionId } : {}),
|
|
298
|
+
...(command.text ? { text: command.text } : {}),
|
|
299
|
+
});
|
|
300
|
+
await emitEvent(ws, config, command.sessionId, command.turnId, {
|
|
301
|
+
type: "option_response",
|
|
302
|
+
optionRequestId: command.optionRequestId,
|
|
303
|
+
...(command.selectedOptionId ? { selectedOptionId: command.selectedOptionId } : {}),
|
|
304
|
+
...(command.text ? { text: command.text } : {}),
|
|
305
|
+
});
|
|
306
|
+
await sendCommandResponse(ws, {
|
|
307
|
+
type: "machine:optionResponded",
|
|
308
|
+
requestId: command.requestId,
|
|
309
|
+
sessionId: command.sessionId,
|
|
310
|
+
turnId: command.turnId,
|
|
311
|
+
optionRequestId: command.optionRequestId,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
function modelFromCommand(value) {
|
|
315
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
316
|
+
}
|
|
261
317
|
export function promptFailureResponse(command, phase, error) {
|
|
262
318
|
const code = promptFailureCode(phase, error);
|
|
263
319
|
return {
|
|
@@ -433,4 +489,68 @@ function truncateOutputPreview(value, maxChars) {
|
|
|
433
489
|
return value;
|
|
434
490
|
return value.slice(value.length - maxChars);
|
|
435
491
|
}
|
|
492
|
+
function normalizePromptAttachments(value) {
|
|
493
|
+
if (!Array.isArray(value))
|
|
494
|
+
return [];
|
|
495
|
+
return value.flatMap((attachment) => {
|
|
496
|
+
if (!attachment ||
|
|
497
|
+
typeof attachment.id !== "string" ||
|
|
498
|
+
typeof attachment.name !== "string" ||
|
|
499
|
+
typeof attachment.mediaType !== "string" ||
|
|
500
|
+
typeof attachment.data !== "string" ||
|
|
501
|
+
typeof attachment.sizeBytes !== "number")
|
|
502
|
+
return [];
|
|
503
|
+
return [{
|
|
504
|
+
id: attachment.id,
|
|
505
|
+
name: attachment.name,
|
|
506
|
+
mediaType: attachment.mediaType,
|
|
507
|
+
sizeBytes: attachment.sizeBytes,
|
|
508
|
+
data: attachment.data,
|
|
509
|
+
}];
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function preparePromptAttachments(text, attachments) {
|
|
513
|
+
const textBlocks = [];
|
|
514
|
+
const runtimeAttachments = [];
|
|
515
|
+
for (const attachment of attachments) {
|
|
516
|
+
const decodedText = decodeTextAttachment(attachment);
|
|
517
|
+
if (decodedText !== undefined) {
|
|
518
|
+
textBlocks.push([
|
|
519
|
+
`--- Attachment: ${attachment.name} (${attachment.mediaType}, ${attachment.sizeBytes} bytes) ---`,
|
|
520
|
+
decodedText,
|
|
521
|
+
"--- End attachment ---",
|
|
522
|
+
].join("\n"));
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
runtimeAttachments.push({
|
|
526
|
+
name: attachment.name,
|
|
527
|
+
mediaType: attachment.mediaType,
|
|
528
|
+
sizeBytes: attachment.sizeBytes,
|
|
529
|
+
data: attachment.data,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
return {
|
|
533
|
+
text: textBlocks.length > 0
|
|
534
|
+
? `${text}\n\nAttached text files:\n\n${textBlocks.join("\n\n")}`
|
|
535
|
+
: text,
|
|
536
|
+
runtimeAttachments,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function decodeTextAttachment(attachment) {
|
|
540
|
+
const mediaType = attachment.mediaType.toLowerCase();
|
|
541
|
+
const textLike = mediaType.startsWith("text/") ||
|
|
542
|
+
mediaType.includes("json") ||
|
|
543
|
+
mediaType.includes("xml") ||
|
|
544
|
+
mediaType.includes("yaml") ||
|
|
545
|
+
mediaType.includes("csv") ||
|
|
546
|
+
/\.(txt|md|markdown|json|jsonl|yaml|yml|csv|tsv|log|xml|html|css|js|ts|tsx|jsx|vue|py|rb|go|rs|java|c|cc|cpp|h|hpp|sh|zsh|bash|ps1)$/iu.test(attachment.name);
|
|
547
|
+
if (!textLike)
|
|
548
|
+
return undefined;
|
|
549
|
+
try {
|
|
550
|
+
return Buffer.from(attachment.data, "base64").toString("utf8").slice(0, 64 * 1024);
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
return undefined;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
436
556
|
//# sourceMappingURL=prompt.js.map
|
|
@@ -31,6 +31,7 @@ export type SessionState = {
|
|
|
31
31
|
agent: string;
|
|
32
32
|
cwd: string;
|
|
33
33
|
name: string;
|
|
34
|
+
model?: string;
|
|
34
35
|
handle: RuntimeHandle;
|
|
35
36
|
historicalBackfill?: HistoricalBackfillState;
|
|
36
37
|
};
|
|
@@ -53,6 +54,7 @@ export type SessionMetadata = {
|
|
|
53
54
|
cwd?: string;
|
|
54
55
|
name?: string;
|
|
55
56
|
providerName?: string;
|
|
57
|
+
model?: string;
|
|
56
58
|
requestedRuntimeName?: string;
|
|
57
59
|
permissionMode?: PermissionMode;
|
|
58
60
|
importedFromRuntimeSessionId?: string;
|
|
@@ -83,6 +85,7 @@ export type SessionMetadata = {
|
|
|
83
85
|
};
|
|
84
86
|
};
|
|
85
87
|
export type SessionFallback = Pick<SessionState, "agent" | "cwd" | "name"> & {
|
|
88
|
+
model?: string;
|
|
86
89
|
emptyRuntimeSession?: boolean;
|
|
87
90
|
historicalBackfill?: HistoricalBackfillState;
|
|
88
91
|
runtime?: SessionMetadata["runtime"];
|
package/apps/daemon/package.json
CHANGED
|
@@ -106,6 +106,7 @@ export function handleControllerMessage(context, connection, message) {
|
|
|
106
106
|
agent: message.agent,
|
|
107
107
|
cwd: message.cwd,
|
|
108
108
|
name: message.name,
|
|
109
|
+
model: message.model,
|
|
109
110
|
encryptedMetadata: message.encryptedMetadata,
|
|
110
111
|
};
|
|
111
112
|
db.exec("BEGIN");
|
|
@@ -158,6 +159,7 @@ export function handleControllerMessage(context, connection, message) {
|
|
|
158
159
|
encryptedPrompt: message.encryptedPrompt,
|
|
159
160
|
encryptedMetadata: sessionRow.encrypted_metadata ? parseJson(sessionRow.encrypted_metadata, undefined) : undefined,
|
|
160
161
|
permissionMode: message.permissionMode,
|
|
162
|
+
model: message.model,
|
|
161
163
|
encryptedMemoryOptions: message.encryptedMemoryOptions,
|
|
162
164
|
timeoutMs: message.timeoutMs,
|
|
163
165
|
};
|
|
@@ -241,6 +243,31 @@ export function handleControllerMessage(context, connection, message) {
|
|
|
241
243
|
sendAck(connection, { type: "server:ack", requestId: message.requestId, ok: true });
|
|
242
244
|
return;
|
|
243
245
|
}
|
|
246
|
+
if (message.type === "controller:optionResponse") {
|
|
247
|
+
if (!allowControllerAction(connection, "session.run", message.requestId))
|
|
248
|
+
return;
|
|
249
|
+
const sessionRow = allowControllerSession(connection, message.sessionId, message.requestId);
|
|
250
|
+
if (!sessionRow)
|
|
251
|
+
return;
|
|
252
|
+
if (rejectDisallowedSessionCommand(sessionRow, message.requestId, "prompt"))
|
|
253
|
+
return;
|
|
254
|
+
const machineConnection = connectedMachine(connection.accountId, sessionRow.machine_id);
|
|
255
|
+
if (!machineConnection) {
|
|
256
|
+
sendMachineOffline(connection, message.requestId);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
send(machineConnection.socket, {
|
|
260
|
+
type: "machine:optionResponse",
|
|
261
|
+
requestId: message.requestId,
|
|
262
|
+
sessionId: message.sessionId,
|
|
263
|
+
turnId: message.turnId,
|
|
264
|
+
optionRequestId: message.optionRequestId,
|
|
265
|
+
selectedOptionId: message.selectedOptionId,
|
|
266
|
+
text: message.text,
|
|
267
|
+
});
|
|
268
|
+
sendAck(connection, { type: "server:ack", requestId: message.requestId, ok: true });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
244
271
|
if (message.type === "controller:reconcileTurn") {
|
|
245
272
|
if (!allowControllerAction(connection, "session.run", message.requestId))
|
|
246
273
|
return;
|
|
@@ -273,6 +273,7 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
|
|
|
273
273
|
agent: body.agent,
|
|
274
274
|
cwd: body.cwd,
|
|
275
275
|
name: body.name,
|
|
276
|
+
model: body.model,
|
|
276
277
|
encryptedMetadata: body.encryptedMetadata,
|
|
277
278
|
};
|
|
278
279
|
context.db.exec("BEGIN");
|
|
@@ -330,6 +331,7 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
|
|
|
330
331
|
encryptedPrompt: body.encryptedPrompt,
|
|
331
332
|
encryptedMetadata: sessionRow.encrypted_metadata ? context.parseJson(sessionRow.encrypted_metadata, undefined) : undefined,
|
|
332
333
|
permissionMode: body.permissionMode,
|
|
334
|
+
model: body.model,
|
|
333
335
|
encryptedMemoryOptions: body.encryptedMemoryOptions,
|
|
334
336
|
timeoutMs: body.timeoutMs,
|
|
335
337
|
};
|
|
@@ -134,6 +134,7 @@ export declare const machineCreateSessionAdmissionSchema: z.ZodObject<{
|
|
|
134
134
|
agent: z.ZodString;
|
|
135
135
|
cwd: z.ZodString;
|
|
136
136
|
name: z.ZodString;
|
|
137
|
+
model: z.ZodOptional<z.ZodString>;
|
|
137
138
|
encryptedMetadata: z.ZodOptional<z.ZodObject<{
|
|
138
139
|
v: z.ZodLiteral<1>;
|
|
139
140
|
alg: z.ZodLiteral<"A256GCM">;
|
|
@@ -170,6 +171,7 @@ export declare const machinePromptAdmissionSchema: z.ZodObject<{
|
|
|
170
171
|
"approve-reads": "approve-reads";
|
|
171
172
|
"deny-all": "deny-all";
|
|
172
173
|
}>>;
|
|
174
|
+
model: z.ZodOptional<z.ZodString>;
|
|
173
175
|
encryptedMemoryOptions: z.ZodOptional<z.ZodObject<{
|
|
174
176
|
v: z.ZodLiteral<1>;
|
|
175
177
|
alg: z.ZodLiteral<"A256GCM">;
|
|
@@ -122,6 +122,7 @@ export const machineCreateSessionAdmissionSchema = z.object({
|
|
|
122
122
|
agent: z.string().min(1),
|
|
123
123
|
cwd: z.string(),
|
|
124
124
|
name: z.string().min(1),
|
|
125
|
+
model: z.string().min(1).optional(),
|
|
125
126
|
encryptedMetadata: encryptedEnvelopeSchema.optional(),
|
|
126
127
|
});
|
|
127
128
|
export const machinePromptAdmissionSchema = z.object({
|
|
@@ -130,6 +131,7 @@ export const machinePromptAdmissionSchema = z.object({
|
|
|
130
131
|
turnId: z.string().min(1),
|
|
131
132
|
encryptedPrompt: encryptedEnvelopeSchema,
|
|
132
133
|
permissionMode: z.enum(["approve-all", "approve-reads", "deny-all"]).optional(),
|
|
134
|
+
model: z.string().min(1).optional(),
|
|
133
135
|
encryptedMemoryOptions: encryptedEnvelopeSchema.optional(),
|
|
134
136
|
timeoutMs: z.number().positive().optional(),
|
|
135
137
|
});
|