@cydm/happy-elves 0.1.0-beta.50 → 0.1.0-beta.52
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/lib/args.js +3 -0
- package/apps/cli/dist/commands/lib/orchestrator.d.ts +2 -0
- package/apps/cli/dist/commands/lib/orchestrator.js +2 -0
- package/apps/cli/dist/commands/lib/scope.d.ts +2 -0
- package/apps/cli/dist/commands/lib/scope.js +18 -0
- package/apps/cli/dist/commands/lib/usage.js +5 -5
- package/apps/cli/dist/commands/orchestrator.js +30 -7
- package/apps/cli/dist/commands/session.js +4 -2
- package/apps/cli/dist/commands/skill.js +4 -2
- package/apps/cli/dist/commands/turn.js +4 -2
- package/apps/daemon/dist/relay/register.js +1 -0
- package/apps/daemon/dist/runtime/external-provider.js +8 -2
- package/apps/daemon/dist/session/lifecycle.js +5 -0
- package/apps/daemon/dist/session/prompt.js +13 -3
- package/apps/daemon/dist/types.d.ts +4 -1
- package/apps/daemon/package.json +1 -1
- package/apps/relay/dist/connections.d.ts +2 -0
- package/apps/relay/dist/controller-handlers.js +32 -1
- package/apps/relay/dist/projections.js +33 -0
- package/apps/relay/dist/relay-context.js +9 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/packages/agent-sdk/dist/index.d.ts +7 -1
- package/packages/client/dist/client.js +4 -0
- package/packages/client/dist/live-types.d.ts +2 -0
- package/packages/client/dist/live.js +2 -0
- package/packages/client/dist/types.d.ts +8 -1
- package/packages/pie-provider/dist/index.js +59 -4
- package/packages/provider-protocol/dist/index.d.ts +67 -0
- package/packages/provider-protocol/dist/index.js +16 -0
- package/packages/runtime/dist/index.d.ts +8 -2
- package/packages/runtime-cli/dist/claude-turn.d.ts +2 -1
- package/packages/runtime-cli/dist/claude-turn.js +31 -3
- package/packages/runtime-cli/dist/codex-app-server.d.ts +11 -3
- package/packages/runtime-cli/dist/codex-app-server.js +143 -21
- package/packages/runtime-cli/dist/codex-protocol.d.ts +5 -3
- package/packages/runtime-cli/dist/codex-protocol.js +23 -5
- package/packages/runtime-cli/dist/index.js +79 -15
- package/packages/runtime-cli/dist/session-store.d.ts +2 -0
- package/packages/runtime-cli/dist/session-store.js +6 -4
- package/packages/shared/dist/protocol-schemas.d.ts +28 -0
- package/packages/shared/dist/protocol-schemas.js +12 -0
- package/packages/shared/dist/protocol-types.d.ts +12 -0
- package/packages/shared/dist/protocol.d.ts +5 -1
- package/packages/shared/dist/protocol.js +4 -0
|
@@ -16,6 +16,7 @@ const valueFlags = new Set([
|
|
|
16
16
|
"machine-id",
|
|
17
17
|
"name",
|
|
18
18
|
"mode",
|
|
19
|
+
"model",
|
|
19
20
|
"output",
|
|
20
21
|
"path",
|
|
21
22
|
"port",
|
|
@@ -49,6 +50,8 @@ const valueFlags = new Set([
|
|
|
49
50
|
"text-file",
|
|
50
51
|
"timeout",
|
|
51
52
|
"timeout-ms",
|
|
53
|
+
"thought-level",
|
|
54
|
+
"thinking",
|
|
52
55
|
"wait-timeout-ms",
|
|
53
56
|
"runtime-timeout",
|
|
54
57
|
"to",
|
|
@@ -6,6 +6,8 @@ export type OrchestratorStatus = "idle" | "inProgress" | "completed" | "needsInp
|
|
|
6
6
|
export type OrchestratorRequestRecord = {
|
|
7
7
|
turnId: string;
|
|
8
8
|
promptSha256: string;
|
|
9
|
+
model?: string | null;
|
|
10
|
+
thoughtLevel?: string | null;
|
|
9
11
|
createdAt: number;
|
|
10
12
|
};
|
|
11
13
|
export type OrchestratorMetadata = {
|
|
@@ -77,6 +77,8 @@ export function readOrchestratorMetadata(metadata) {
|
|
|
77
77
|
requests[requestId] = {
|
|
78
78
|
turnId: candidate.turnId,
|
|
79
79
|
promptSha256: candidate.promptSha256,
|
|
80
|
+
...(candidate.model === null || typeof candidate.model === "string" ? { model: candidate.model } : {}),
|
|
81
|
+
...(candidate.thoughtLevel === null || typeof candidate.thoughtLevel === "string" ? { thoughtLevel: candidate.thoughtLevel } : {}),
|
|
80
82
|
createdAt: candidate.createdAt,
|
|
81
83
|
};
|
|
82
84
|
}
|
|
@@ -9,6 +9,8 @@ export declare function requireScopedLoop(scope: ScopedTokenScope | undefined, l
|
|
|
9
9
|
export declare function loopVisibleToScope(loop: LoopRecord, scope: ScopedTokenScope | undefined, tokenId?: string, machineId?: string | undefined): boolean;
|
|
10
10
|
export declare function parseEventCursor(value: string | boolean | undefined): `cursor_${number}` | number | undefined;
|
|
11
11
|
export declare function parsePermissionMode(value: string | boolean | undefined): PermissionMode | undefined;
|
|
12
|
+
export declare function parseRuntimeSelection(value: string | boolean | undefined, label: string): string | null | undefined;
|
|
13
|
+
export declare function parseThoughtLevel(flags: Record<string, string | boolean>): string | null | undefined;
|
|
12
14
|
export declare function parseControllerJoinUrl(value: string): {
|
|
13
15
|
relayUrl: string;
|
|
14
16
|
code: string;
|
|
@@ -82,6 +82,24 @@ export function parsePermissionMode(value) {
|
|
|
82
82
|
return "approve-reads";
|
|
83
83
|
throw new CliError(`Invalid permission mode: ${value}`, "INVALID_ARGUMENT");
|
|
84
84
|
}
|
|
85
|
+
export function parseRuntimeSelection(value, label) {
|
|
86
|
+
if (value === undefined || value === false)
|
|
87
|
+
return undefined;
|
|
88
|
+
if (value === true)
|
|
89
|
+
throw new CliError(`Missing --${label} value`, "MISSING_ARGUMENT");
|
|
90
|
+
const normalized = value.trim();
|
|
91
|
+
if (!normalized)
|
|
92
|
+
throw new CliError(`Invalid --${label}: empty value`, "INVALID_ARGUMENT");
|
|
93
|
+
return normalized.toLowerCase() === "default" ? null : normalized;
|
|
94
|
+
}
|
|
95
|
+
export function parseThoughtLevel(flags) {
|
|
96
|
+
const canonical = flags["thought-level"];
|
|
97
|
+
const alias = flags.thinking;
|
|
98
|
+
if (canonical !== undefined && canonical !== false && alias !== undefined && alias !== false) {
|
|
99
|
+
throw new CliError("Use only one of --thought-level or --thinking", "INVALID_ARGUMENT");
|
|
100
|
+
}
|
|
101
|
+
return parseRuntimeSelection(canonical !== undefined && canonical !== false ? canonical : alias, canonical !== undefined && canonical !== false ? "thought-level" : "thinking");
|
|
102
|
+
}
|
|
85
103
|
export function parseControllerJoinUrl(value) {
|
|
86
104
|
let url;
|
|
87
105
|
try {
|
|
@@ -47,7 +47,7 @@ Check local controller config, relay reachability, and daemon state.
|
|
|
47
47
|
orchestrator: `Usage:
|
|
48
48
|
happy-elves orchestrator list-threads [--query <q>] [--tag <tag>] [--machine <id>] [--limit n] [--all] --json
|
|
49
49
|
happy-elves orchestrator read-thread <threadId> [--messages latest|all] [--limit n] [--include-output] --json
|
|
50
|
-
happy-elves orchestrator send-message <threadId> (--text <prompt> | --text-file <path>) --request-id <id> [--permission-mode <mode>] [--runtime-timeout 1000s] --json
|
|
50
|
+
happy-elves orchestrator send-message <threadId> (--text <prompt> | --text-file <path>) --request-id <id> [--permission-mode <mode>] [--model <key>] [--thought-level <key>|--thinking <key>] [--runtime-timeout 1000s] --json
|
|
51
51
|
happy-elves orchestrator wait-thread <threadId> --turn <turnId> [--timeout 1000s] --json
|
|
52
52
|
happy-elves orchestrator set-thread-tags <threadId> --tags worker,reviewer --json
|
|
53
53
|
|
|
@@ -57,7 +57,7 @@ Codex-like orchestration:
|
|
|
57
57
|
for that exact turn id instead of following the latest turn.
|
|
58
58
|
`,
|
|
59
59
|
session: `Usage:
|
|
60
|
-
happy-elves session create --machine <machineId> [--agent codex] [--cwd <path>] [--name <name>] [--permission-mode <mode>] --json
|
|
60
|
+
happy-elves session create --machine <machineId> [--agent codex] [--cwd <path>] [--name <name>] [--permission-mode <mode>] [--model <key>] [--thought-level <key>|--thinking <key>] --json
|
|
61
61
|
happy-elves session list [--machine <machineId>] [--agent <agent>] [--cwd <cwd>] [--name <name>] [--limit n] [--all] [--verbose] [--json]
|
|
62
62
|
happy-elves session history --machine <machineId> [--cwd <path>] [--agent <agent>] [--include-archived] [--limit 20] --json
|
|
63
63
|
happy-elves session continue <runtimeSessionId> --machine <machineId> [--name <name>] --json
|
|
@@ -85,7 +85,7 @@ Session management:
|
|
|
85
85
|
happy-elves turn list <sessionId> [--limit 10] [--verbose] [--json]
|
|
86
86
|
happy-elves turn status <sessionId> <turnId> [--verbose] [--json]
|
|
87
87
|
happy-elves turn result <sessionId> [<turnId>] [--verbose] [--json]
|
|
88
|
-
happy-elves turn run <sessionId> (--text <prompt> | --text-file <path>) [--detach] [--timeout 1000s] [--runtime-timeout 1000s] [--permission-mode approve-reads|approve-all|deny-all] [--json]
|
|
88
|
+
happy-elves turn run <sessionId> (--text <prompt> | --text-file <path>) [--detach] [--timeout 1000s] [--runtime-timeout 1000s] [--permission-mode approve-reads|approve-all|deny-all] [--model <key>] [--thought-level <key>|--thinking <key>] [--json]
|
|
89
89
|
happy-elves turn wait <sessionId> <turnId> [--timeout 1000s] --json
|
|
90
90
|
happy-elves turn cancel <sessionId> <turnId> [--reason <reason>] --json
|
|
91
91
|
happy-elves turn rewind <sessionId> <turn-or-checkpoint-id> --json
|
|
@@ -260,8 +260,8 @@ Output modes:
|
|
|
260
260
|
thread's history is unavailable, the item reports historyStatus/historyError
|
|
261
261
|
instead of pretending to be healthy inProgress.
|
|
262
262
|
send-message requires --request-id and is detached by default. Reusing the
|
|
263
|
-
same request id with the same prompt returns the same turn;
|
|
264
|
-
different prompt returns REQUEST_ID_CONFLICT.
|
|
263
|
+
same request id with the same prompt/model/thinking returns the same turn;
|
|
264
|
+
reusing it with different prompt/model/thinking returns REQUEST_ID_CONFLICT.
|
|
265
265
|
|
|
266
266
|
Debug commands:
|
|
267
267
|
machine/session/turn commands remain available for lower-level inspection,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliError, ControllerClient, DEFAULT_COMMAND_TIMEOUT_MS, collectExactTurnEvents, compactText, conciseEvents, deterministicRelayRequestId, deterministicTurnId, deriveOrchestration, normalizeOrchestratorStatus, normalizeOrchestratorTag, normalizeOrchestratorTags, ok, parseDurationMs, parsePermissionMode, promptSha256, readConfig, readOrchestratorMetadata, readPromptTextFile, rebuildTurnOutput, requirePositional, requireString, summarizeTurns, withOrchestratorRequest, withOrchestratorTags, } from "./lib/index.js";
|
|
1
|
+
import { CliError, ControllerClient, DEFAULT_COMMAND_TIMEOUT_MS, collectExactTurnEvents, compactText, conciseEvents, deterministicRelayRequestId, deterministicTurnId, deriveOrchestration, normalizeOrchestratorStatus, normalizeOrchestratorTag, normalizeOrchestratorTags, ok, parseDurationMs, parsePermissionMode, parseRuntimeSelection, parseThoughtLevel, promptSha256, readConfig, readOrchestratorMetadata, readPromptTextFile, rebuildTurnOutput, requirePositional, requireString, summarizeTurns, withOrchestratorRequest, withOrchestratorTags, } from "./lib/index.js";
|
|
2
2
|
const defaultTurnHistoryLimit = 1000;
|
|
3
3
|
const defaultMessageLimit = 50;
|
|
4
4
|
class OrchestratorError extends CliError {
|
|
@@ -101,15 +101,28 @@ async function readThreadContext(client, threadId) {
|
|
|
101
101
|
}
|
|
102
102
|
return { machines: snapshot.machines, session };
|
|
103
103
|
}
|
|
104
|
-
function requestRecordForExistingTurn(turn, hash, createdAt) {
|
|
104
|
+
function requestRecordForExistingTurn(turn, hash, selection, createdAt) {
|
|
105
105
|
if (!turn)
|
|
106
106
|
return undefined;
|
|
107
107
|
return {
|
|
108
108
|
turnId: turn.turnId,
|
|
109
109
|
promptSha256: hash,
|
|
110
|
+
...selectionRecordFields(selection),
|
|
110
111
|
createdAt: turn.startedAt ?? turn.updatedAt ?? createdAt,
|
|
111
112
|
};
|
|
112
113
|
}
|
|
114
|
+
function selectionRecordFields(selection) {
|
|
115
|
+
return {
|
|
116
|
+
...(selection.model !== undefined ? { model: selection.model } : {}),
|
|
117
|
+
...(selection.thoughtLevel !== undefined ? { thoughtLevel: selection.thoughtLevel } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function sameRequestRecord(existing, expected) {
|
|
121
|
+
return existing.turnId === expected.turnId &&
|
|
122
|
+
existing.promptSha256 === expected.promptSha256 &&
|
|
123
|
+
existing.model === expected.model &&
|
|
124
|
+
existing.thoughtLevel === expected.thoughtLevel;
|
|
125
|
+
}
|
|
113
126
|
function errorCode(error) {
|
|
114
127
|
const candidate = error;
|
|
115
128
|
return typeof candidate?.code === "string" ? candidate.code : undefined;
|
|
@@ -148,7 +161,7 @@ async function persistRequestRecordBestEffort(client, threadId, requestId, recor
|
|
|
148
161
|
const metadata = await client.decodeSessionMetadata(session);
|
|
149
162
|
const existing = readOrchestratorMetadata(metadata).requests?.[requestId];
|
|
150
163
|
if (existing) {
|
|
151
|
-
if (existing
|
|
164
|
+
if (sameRequestRecord(existing, record))
|
|
152
165
|
return;
|
|
153
166
|
return;
|
|
154
167
|
}
|
|
@@ -172,7 +185,7 @@ function terminalStatusForTurn(turn) {
|
|
|
172
185
|
return "inProgress";
|
|
173
186
|
}
|
|
174
187
|
function requestConflict(threadId, requestId, turnId) {
|
|
175
|
-
return new OrchestratorError(`Request id already exists with different prompt
|
|
188
|
+
return new OrchestratorError(`Request id already exists with different prompt, model, or thoughtLevel: ${requestId}`, "REQUEST_ID_CONFLICT", { retryable: false, sessionId: threadId, turnId });
|
|
176
189
|
}
|
|
177
190
|
function threadBusy(threadId, active, latestCompleted) {
|
|
178
191
|
return new OrchestratorError(`Thread already has a running turn: ${active.turnId}`, "THREAD_BUSY", {
|
|
@@ -284,11 +297,19 @@ export async function handleOrchestrator({ domain, action, positional, flags })
|
|
|
284
297
|
const turnId = deterministicTurnId(threadId, requestId);
|
|
285
298
|
const relayRequestId = deterministicRelayRequestId(threadId, requestId);
|
|
286
299
|
const hash = promptSha256(text);
|
|
300
|
+
const model = parseRuntimeSelection(flags.model, "model");
|
|
301
|
+
const thoughtLevel = parseThoughtLevel(flags);
|
|
302
|
+
const selection = selectionRecordFields({ model, thoughtLevel });
|
|
303
|
+
const record = { turnId, promptSha256: hash, ...selection, createdAt: Date.now() };
|
|
287
304
|
const self = await client.tokenSelf();
|
|
288
305
|
if (!self.full) {
|
|
289
306
|
throw new OrchestratorError("orchestrator send-message requires a full controller token.", "SCOPED_TOKEN_FORBIDDEN", { retryable: false, sessionId: threadId, turnId });
|
|
290
307
|
}
|
|
291
|
-
await readThreadContext(client, threadId);
|
|
308
|
+
const { session } = await readThreadContext(client, threadId);
|
|
309
|
+
const metadata = await client.decodeSessionMetadata(session);
|
|
310
|
+
const existingRequest = readOrchestratorMetadata(metadata).requests?.[requestId];
|
|
311
|
+
if (existingRequest && !sameRequestRecord(existingRequest, record))
|
|
312
|
+
throw requestConflict(threadId, requestId, turnId);
|
|
292
313
|
const events = await client.history(threadId, defaultTurnHistoryLimit);
|
|
293
314
|
const turns = summarizeTurns(events);
|
|
294
315
|
const existingTurn = await readExactTurn(client, threadId, turnId);
|
|
@@ -299,9 +320,10 @@ export async function handleOrchestrator({ domain, action, positional, flags })
|
|
|
299
320
|
if (active && active.turnId !== turnId)
|
|
300
321
|
throw threadBusy(threadId, active, latestCompleted);
|
|
301
322
|
if (existingTurn && existingTurn.status !== "running") {
|
|
302
|
-
await persistRequestRecordBestEffort(client, threadId, requestId, requestRecordForExistingTurn(existingTurn, hash, Date.now()) ?? {
|
|
323
|
+
await persistRequestRecordBestEffort(client, threadId, requestId, requestRecordForExistingTurn(existingTurn, hash, selection, Date.now()) ?? {
|
|
303
324
|
turnId,
|
|
304
325
|
promptSha256: hash,
|
|
326
|
+
...selection,
|
|
305
327
|
createdAt: Date.now(),
|
|
306
328
|
});
|
|
307
329
|
ok("orchestrator.sendMessage", {
|
|
@@ -326,7 +348,6 @@ export async function handleOrchestrator({ domain, action, positional, flags })
|
|
|
326
348
|
}, { requestId, sessionId: threadId, turnId });
|
|
327
349
|
return true;
|
|
328
350
|
}
|
|
329
|
-
const record = { turnId, promptSha256: hash, createdAt: Date.now() };
|
|
330
351
|
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
331
352
|
const runtimeTimeoutMs = parseDurationMs(flags["runtime-timeout"], DEFAULT_COMMAND_TIMEOUT_MS);
|
|
332
353
|
try {
|
|
@@ -336,6 +357,8 @@ export async function handleOrchestrator({ domain, action, positional, flags })
|
|
|
336
357
|
requestId: relayRequestId,
|
|
337
358
|
turnId,
|
|
338
359
|
promptHash: hash,
|
|
360
|
+
model,
|
|
361
|
+
thoughtLevel,
|
|
339
362
|
ackTimeoutMs: 15_000,
|
|
340
363
|
runtimeTimeoutMs,
|
|
341
364
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliError, ControllerClient, compareSessionEventLogicalOrder, compactSessionListItem, compactText, conciseSessionSummary, deriveOrchestration, listWorkspaceHistoricalSessions, ok, parsePermissionMode, readConfig, readStoredTurnOutput, requirePositional, requireString, showSession, structuredSessionProjection, summarizeTurns, wantsJson, wantsVerbose } from "./lib/index.js";
|
|
1
|
+
import { CliError, ControllerClient, compareSessionEventLogicalOrder, compactSessionListItem, compactText, conciseSessionSummary, deriveOrchestration, listWorkspaceHistoricalSessions, ok, parsePermissionMode, parseRuntimeSelection, parseThoughtLevel, readConfig, readStoredTurnOutput, requirePositional, requireString, showSession, structuredSessionProjection, summarizeTurns, wantsJson, wantsVerbose } from "./lib/index.js";
|
|
2
2
|
function formatTurnTimestamp(value) {
|
|
3
3
|
return value === undefined ? "-" : new Date(value).toISOString();
|
|
4
4
|
}
|
|
@@ -355,8 +355,10 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
355
355
|
const requestedRuntimeName = typeof flags.name === "string" ? flags.name : undefined;
|
|
356
356
|
const name = "main";
|
|
357
357
|
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
358
|
+
const model = parseRuntimeSelection(flags.model, "model");
|
|
359
|
+
const thoughtLevel = parseThoughtLevel(flags);
|
|
358
360
|
const client = await makeClient();
|
|
359
|
-
const result = await client.createSession({ machineId, agent, cwd, name, runtimeName: requestedRuntimeName, permissionMode });
|
|
361
|
+
const result = await client.createSession({ machineId, agent, cwd, name, runtimeName: requestedRuntimeName, permissionMode, model, thoughtLevel });
|
|
360
362
|
const session = result.sessionId ? await client.getSession(result.sessionId) : undefined;
|
|
361
363
|
const projectedSession = session
|
|
362
364
|
? wantsVerbose(flags)
|
|
@@ -42,8 +42,10 @@ happy-elves orchestrator wait-thread <threadId> --turn <turnId> --json
|
|
|
42
42
|
Important rules:
|
|
43
43
|
- \`threadId\` is the same value as Happy Elves \`sessionId\`.
|
|
44
44
|
- Use stable request ids such as \`orch_round_3_worker_fix\`.
|
|
45
|
-
- Reusing the same request id with the same prompt returns the same turn.
|
|
46
|
-
- Reusing the same request id with different prompt text returns \`REQUEST_ID_CONFLICT\`.
|
|
45
|
+
- Reusing the same request id with the same prompt/model/thinking returns the same turn.
|
|
46
|
+
- Reusing the same request id with different prompt text, model, or thinking returns \`REQUEST_ID_CONFLICT\`.
|
|
47
|
+
- To override runtime selection, pass \`--model <key>\` and \`--thought-level <key>\`; \`--thinking <key>\` is an alias. Use \`default\` to return to provider defaults.
|
|
48
|
+
- Runtime failures such as \`CODEX_EMPTY_COMPLETION\` are terminal failed turns; inspect \`wait-thread --turn\` or \`turn result\` for the full error text.
|
|
47
49
|
- If \`send-message\` returns \`THREAD_BUSY\`, wait/read the active turn instead of sending another prompt.
|
|
48
50
|
- For waiting, use the exact \`turnId\` returned by \`send-message\`; do not follow "latest" in multi-actor workflows.
|
|
49
51
|
- After installing or updating this skill, open a fresh Codex thread or reload skills before relying on the new instructions.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliError, ControllerClient, DEFAULT_COMMAND_TIMEOUT_MS, collectExactTurnEvents, compactText, displayPath, ok, parseDurationMs, parsePermissionMode, printConciseEvents, readConfig, readPromptTextFile, rebuildTurnOutput, requirePositional, requireProjectedSession, requireString, structuredOutputForRunningTurn, structuredOutputForStoredTurn, structuredOutputForTurn, summarizeTurns, wantsJson, wantsVerbose, writeTurnOutput } from "./lib/index.js";
|
|
1
|
+
import { CliError, ControllerClient, DEFAULT_COMMAND_TIMEOUT_MS, collectExactTurnEvents, compactText, displayPath, ok, parseDurationMs, parsePermissionMode, parseRuntimeSelection, parseThoughtLevel, printConciseEvents, readConfig, readPromptTextFile, rebuildTurnOutput, requirePositional, requireProjectedSession, requireString, structuredOutputForRunningTurn, structuredOutputForStoredTurn, structuredOutputForTurn, summarizeTurns, wantsJson, wantsVerbose, writeTurnOutput } from "./lib/index.js";
|
|
2
2
|
function formatTurnTimestamp(value) {
|
|
3
3
|
return value === undefined ? "-" : new Date(value).toISOString();
|
|
4
4
|
}
|
|
@@ -120,11 +120,13 @@ export async function handleTurn({ domain, action, positional, flags }) {
|
|
|
120
120
|
if (!text.trim())
|
|
121
121
|
throw new CliError("Missing --text or --text-file", "MISSING_ARGUMENT");
|
|
122
122
|
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
123
|
+
const model = parseRuntimeSelection(flags.model, "model");
|
|
124
|
+
const thoughtLevel = parseThoughtLevel(flags);
|
|
123
125
|
const wait = flags.detach !== true && flags["no-wait"] !== true;
|
|
124
126
|
const watchTimeoutMs = parseDurationMs(flags.timeout, DEFAULT_COMMAND_TIMEOUT_MS);
|
|
125
127
|
const ackTimeoutMs = 15_000;
|
|
126
128
|
const runtimeTimeoutMs = parseDurationMs(flags["runtime-timeout"], DEFAULT_COMMAND_TIMEOUT_MS);
|
|
127
|
-
const result = await client.run(sessionId, text, { wait, permissionMode, waitTimeoutMs: watchTimeoutMs, ackTimeoutMs, runtimeTimeoutMs });
|
|
129
|
+
const result = await client.run(sessionId, text, { wait, permissionMode, model, thoughtLevel, waitTimeoutMs: watchTimeoutMs, ackTimeoutMs, runtimeTimeoutMs });
|
|
128
130
|
const events = await client.decodeEvents(result.events);
|
|
129
131
|
const output = wait && result.turnId ? await writeTurnOutput(sessionId, result.turnId, events) : undefined;
|
|
130
132
|
if (wantsJson(flags)) {
|
|
@@ -24,6 +24,7 @@ function agentProfileForRegister(agent) {
|
|
|
24
24
|
...(agent.command ? { command: agent.command } : {}),
|
|
25
25
|
...(agent.historyModel ? { historyModel: agent.historyModel } : {}),
|
|
26
26
|
...(agent.sessionPrimitives ? { sessionPrimitives: runtimeAgentPrimitives(agent.sessionPrimitives) } : {}),
|
|
27
|
+
...(agent.runtimeOptions ? { runtimeOptions: agent.runtimeOptions } : {}),
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
function runtimeAgentPrimitives(primitives) {
|
|
@@ -10,7 +10,7 @@ const maxJsonLineChars = 1024 * 1024;
|
|
|
10
10
|
const stderrRingChars = 16 * 1024;
|
|
11
11
|
const historicalEventPageLimit = 200;
|
|
12
12
|
const historicalEventPageSafetyLimit = 1_000;
|
|
13
|
-
const pieMinimumVersion = "1.0.
|
|
13
|
+
const pieMinimumVersion = "1.0.21";
|
|
14
14
|
const pieAutoProbeTimeoutMs = 5_000;
|
|
15
15
|
export function externalProviderConfigsFromEnv() {
|
|
16
16
|
const raw = process.env.HAPPY_ELVES_AGENT_PROVIDERS_JSON?.trim();
|
|
@@ -74,11 +74,12 @@ function pieCommandSupportsProvider(command) {
|
|
|
74
74
|
const listResult = runPieProbe(command, ["sessions", "list", "--json", "--limit", "1"]);
|
|
75
75
|
if (listResult.status !== 0)
|
|
76
76
|
return false;
|
|
77
|
-
const modelsResult = runPieProbe(command, ["models", "validate"]);
|
|
77
|
+
const modelsResult = runPieProbe(command, ["models", "validate", "--json"]);
|
|
78
78
|
if (modelsResult.status !== 0)
|
|
79
79
|
return false;
|
|
80
80
|
try {
|
|
81
81
|
JSON.parse(listResult.stdout);
|
|
82
|
+
JSON.parse(modelsResult.stdout);
|
|
82
83
|
return true;
|
|
83
84
|
}
|
|
84
85
|
catch {
|
|
@@ -315,6 +316,7 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
315
316
|
rename: agent.capabilities.sessionPrimitives.rename ?? "unsupported",
|
|
316
317
|
}
|
|
317
318
|
: { resume: "unsupported", rewind: "unsupported", fork: "unsupported", rename: "unsupported" },
|
|
319
|
+
runtimeOptions: agent.capabilities?.runtimeOptions,
|
|
318
320
|
}));
|
|
319
321
|
const agentsById = new Map();
|
|
320
322
|
for (const agent of externalAgents)
|
|
@@ -338,6 +340,8 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
338
340
|
...(input.resumeHandle ? { resumeHandle: sdkHandle(input.resumeHandle) } : {}),
|
|
339
341
|
cwd: input.cwd,
|
|
340
342
|
permissionMode: input.permissionMode,
|
|
343
|
+
model: input.model,
|
|
344
|
+
thoughtLevel: input.thoughtLevel,
|
|
341
345
|
restartableEmptySession: input.restartableEmptySession,
|
|
342
346
|
}), "ensureSession result handle");
|
|
343
347
|
return toRuntimeHandle(input.sessionKey, handle, input.cwd);
|
|
@@ -429,6 +433,8 @@ export function createExternalAgentRuntimeProvider(options) {
|
|
|
429
433
|
attachments: input.attachments,
|
|
430
434
|
mode: input.mode,
|
|
431
435
|
permissionMode: input.permissionMode,
|
|
436
|
+
model: input.model,
|
|
437
|
+
thoughtLevel: input.thoughtLevel,
|
|
432
438
|
requestId: input.requestId,
|
|
433
439
|
timeoutMs: input.timeoutMs,
|
|
434
440
|
});
|
|
@@ -22,6 +22,8 @@ export async function handleCreateSession(ws, config, command) {
|
|
|
22
22
|
mode: "persistent",
|
|
23
23
|
cwd,
|
|
24
24
|
permissionMode: metadata?.permissionMode,
|
|
25
|
+
model: metadata?.modelMode,
|
|
26
|
+
thoughtLevel: metadata?.thoughtLevel,
|
|
25
27
|
});
|
|
26
28
|
let providerName;
|
|
27
29
|
const requestedRuntimeName = metadata?.requestedRuntimeName;
|
|
@@ -68,6 +70,9 @@ export async function handleCreateSession(ws, config, command) {
|
|
|
68
70
|
...(providerName ? { name: providerName } : {}),
|
|
69
71
|
encryptedMetadata: await encryptedSessionMetadata(config, command.sessionId, state, {
|
|
70
72
|
emptyRuntimeSession: true,
|
|
73
|
+
modelMode: metadata?.modelMode,
|
|
74
|
+
thoughtLevel: metadata?.thoughtLevel,
|
|
75
|
+
runtimeOptions: handle.runtimeOptions,
|
|
71
76
|
...(providerName ? { providerName, renamedAt: new Date().toISOString() } : {}),
|
|
72
77
|
readyAt: new Date().toISOString(),
|
|
73
78
|
}),
|
|
@@ -6,7 +6,7 @@ import { activePromptRequests, completedPromptRequests, sessions, sessionTurns }
|
|
|
6
6
|
import { attachClaimedTurn, claimSessionTurn, releaseSessionTurn, sendCommandResponse, sendReliable } from "../relay/send.js";
|
|
7
7
|
import { emitEvent, runtimeEventToPayload } from "./events.js";
|
|
8
8
|
import { replaceAuthoritativeTranscript, resumeSession, usesAuthoritativeTranscript } from "./lifecycle.js";
|
|
9
|
-
import { encryptedSessionMetadata, sessionFallbackFromCommand } from "./metadata.js";
|
|
9
|
+
import { commandSessionMetadata, encryptedSessionMetadata, sessionFallbackFromCommand } from "./metadata.js";
|
|
10
10
|
export async function handlePrompt(ws, config, command) {
|
|
11
11
|
if (command.type !== "machine:prompt")
|
|
12
12
|
return;
|
|
@@ -47,6 +47,9 @@ export async function handlePrompt(ws, config, command) {
|
|
|
47
47
|
};
|
|
48
48
|
try {
|
|
49
49
|
const previousBackendSessionId = session.handle.backendSessionId;
|
|
50
|
+
const previousMetadata = await commandSessionMetadata(config, command.encryptedMetadata);
|
|
51
|
+
const effectiveModel = command.model !== undefined ? command.model : previousMetadata?.modelMode;
|
|
52
|
+
const effectiveThoughtLevel = command.thoughtLevel !== undefined ? command.thoughtLevel : previousMetadata?.thoughtLevel;
|
|
50
53
|
let result;
|
|
51
54
|
try {
|
|
52
55
|
const prompt = await decryptJson(config.accountSecret, command.encryptedPrompt);
|
|
@@ -60,6 +63,8 @@ export async function handlePrompt(ws, config, command) {
|
|
|
60
63
|
text,
|
|
61
64
|
mode: "prompt",
|
|
62
65
|
permissionMode: command.permissionMode,
|
|
66
|
+
model: effectiveModel,
|
|
67
|
+
thoughtLevel: effectiveThoughtLevel,
|
|
63
68
|
requestId: command.requestId,
|
|
64
69
|
timeoutMs: command.timeoutMs,
|
|
65
70
|
});
|
|
@@ -78,6 +83,8 @@ export async function handlePrompt(ws, config, command) {
|
|
|
78
83
|
turnId: command.turnId,
|
|
79
84
|
promptChars: text.length,
|
|
80
85
|
permissionMode: command.permissionMode ?? "approve-reads",
|
|
86
|
+
...(effectiveModel !== undefined ? { model: effectiveModel } : {}),
|
|
87
|
+
...(effectiveThoughtLevel !== undefined ? { thoughtLevel: effectiveThoughtLevel } : {}),
|
|
81
88
|
},
|
|
82
89
|
});
|
|
83
90
|
pumpRuntimeEvents(turn, ws, config, command);
|
|
@@ -94,7 +101,7 @@ export async function handlePrompt(ws, config, command) {
|
|
|
94
101
|
evidence: { requestId: command.requestId, turnId: command.turnId, error: errorMessage(error) },
|
|
95
102
|
});
|
|
96
103
|
}
|
|
97
|
-
const refreshedMetadata = await refreshedRuntimeMetadata(config, command, session, result);
|
|
104
|
+
const refreshedMetadata = await refreshedRuntimeMetadata(config, command, session, result, previousMetadata, effectiveModel, effectiveThoughtLevel);
|
|
98
105
|
if (session.handle.backendSessionId && session.handle.backendSessionId !== previousBackendSessionId) {
|
|
99
106
|
await appendAudit({
|
|
100
107
|
sessionId: command.sessionId,
|
|
@@ -171,13 +178,16 @@ function pumpRuntimeEvents(turn, ws, config, command) {
|
|
|
171
178
|
}
|
|
172
179
|
})();
|
|
173
180
|
}
|
|
174
|
-
async function refreshedRuntimeMetadata(config, command, session, result) {
|
|
181
|
+
async function refreshedRuntimeMetadata(config, command, session, result, previousMetadata, modelMode, thoughtLevel) {
|
|
175
182
|
if (!session.handle.backendSessionId)
|
|
176
183
|
return undefined;
|
|
177
184
|
try {
|
|
178
185
|
return await encryptedSessionMetadata(config, command.sessionId, session, {
|
|
179
186
|
currentHead: result.currentHead,
|
|
180
187
|
emptyRuntimeSession: result.status === "failed" && !result.currentHead && !result.lastTurnId,
|
|
188
|
+
...(modelMode !== undefined ? { modelMode } : {}),
|
|
189
|
+
...(thoughtLevel !== undefined ? { thoughtLevel } : {}),
|
|
190
|
+
...(previousMetadata?.runtimeOptions !== undefined ? { runtimeOptions: previousMetadata.runtimeOptions } : {}),
|
|
181
191
|
runtimeMetadataRefreshedAt: new Date().toISOString(),
|
|
182
192
|
});
|
|
183
193
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SessionHeadAdvanceBasis } from "../../../packages/shared/dist/index.js";
|
|
2
|
-
import type { RuntimeHandle, RuntimeTurn } from "../../../packages/runtime/dist/index.js";
|
|
2
|
+
import type { RuntimeHandle, RuntimeOptions, RuntimeTurn } from "../../../packages/runtime/dist/index.js";
|
|
3
3
|
export type DirectoryListResult = {
|
|
4
4
|
machineId: string;
|
|
5
5
|
path: string;
|
|
@@ -57,6 +57,9 @@ export type SessionMetadata = {
|
|
|
57
57
|
providerName?: string;
|
|
58
58
|
requestedRuntimeName?: string;
|
|
59
59
|
permissionMode?: PermissionMode;
|
|
60
|
+
modelMode?: string | null;
|
|
61
|
+
thoughtLevel?: string | null;
|
|
62
|
+
runtimeOptions?: RuntimeOptions;
|
|
60
63
|
importedFromRuntimeSessionId?: string;
|
|
61
64
|
importedSummary?: string;
|
|
62
65
|
sourceSessionId?: string;
|
package/apps/daemon/package.json
CHANGED
|
@@ -9,6 +9,7 @@ export type PendingCommandContext = {
|
|
|
9
9
|
command?: string;
|
|
10
10
|
cwd?: string;
|
|
11
11
|
machineId: string;
|
|
12
|
+
model?: string | null;
|
|
12
13
|
name?: string;
|
|
13
14
|
promptHash?: string;
|
|
14
15
|
recipientDeviceId?: string;
|
|
@@ -19,6 +20,7 @@ export type PendingCommandContext = {
|
|
|
19
20
|
sourceSessionId?: string;
|
|
20
21
|
targetSessionId?: string;
|
|
21
22
|
timeoutMs?: number;
|
|
23
|
+
thoughtLevel?: string | null;
|
|
22
24
|
turnId?: string;
|
|
23
25
|
};
|
|
24
26
|
export declare function machineKey(accountId: string, machineId: string): string;
|
|
@@ -169,12 +169,16 @@ export function handleControllerMessage(context, connection, message) {
|
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
171
|
rememberAcceptedRun(connection, { machineId: sessionRow.machine_id, sessionId: sessionRow.id, turnId: message.turnId });
|
|
172
|
+
const model = normalizeRuntimeSelectionContext(message.model);
|
|
173
|
+
const thoughtLevel = normalizeRuntimeSelectionContext(message.thoughtLevel);
|
|
172
174
|
markCommandPending(connection.accountId, sessionRow.machine_id, message.requestId, {
|
|
173
175
|
action: "session.run",
|
|
174
176
|
blocksSessionTurn: true,
|
|
177
|
+
model,
|
|
175
178
|
promptHash: message.promptHash,
|
|
176
179
|
recipientDeviceId: connection.deviceId ?? undefined,
|
|
177
180
|
sessionId: sessionRow.id,
|
|
181
|
+
thoughtLevel,
|
|
178
182
|
turnId: message.turnId,
|
|
179
183
|
});
|
|
180
184
|
sendAck(connection, { type: "server:ack", requestId: message.requestId, ok: true, machineId: sessionRow.machine_id, sessionId: sessionRow.id, action: "session.run" });
|
|
@@ -198,6 +202,8 @@ export function handleControllerMessage(context, connection, message) {
|
|
|
198
202
|
encryptedPrompt: message.encryptedPrompt,
|
|
199
203
|
encryptedMetadata: sessionRow.encrypted_metadata ? parseJson(sessionRow.encrypted_metadata, undefined) : undefined,
|
|
200
204
|
permissionMode: message.permissionMode,
|
|
205
|
+
model,
|
|
206
|
+
thoughtLevel,
|
|
201
207
|
timeoutMs: message.timeoutMs,
|
|
202
208
|
});
|
|
203
209
|
return;
|
|
@@ -730,7 +736,15 @@ function controllerMessageCommandContext(message, targetMachineId) {
|
|
|
730
736
|
case "controller:createSession":
|
|
731
737
|
return { action: "session.create", agent: message.agent, cwd: message.cwd, machineId: message.machineId, name: message.name };
|
|
732
738
|
case "controller:prompt":
|
|
733
|
-
return {
|
|
739
|
+
return {
|
|
740
|
+
action: "session.run",
|
|
741
|
+
machineId: targetMachineId,
|
|
742
|
+
model: normalizeRuntimeSelectionContext(message.model),
|
|
743
|
+
promptHash: message.promptHash,
|
|
744
|
+
sessionId: message.sessionId,
|
|
745
|
+
thoughtLevel: normalizeRuntimeSelectionContext(message.thoughtLevel),
|
|
746
|
+
turnId: message.turnId,
|
|
747
|
+
};
|
|
734
748
|
case "controller:cancel":
|
|
735
749
|
return { action: "session.cancel", machineId: targetMachineId, sessionId: message.sessionId, turnId: message.turnId };
|
|
736
750
|
case "controller:resume":
|
|
@@ -791,6 +805,8 @@ function pendingCommandMatchesMessage(pending, expected) {
|
|
|
791
805
|
return false;
|
|
792
806
|
if (!matchesDefined(pending.name, expected.name))
|
|
793
807
|
return false;
|
|
808
|
+
if (expected.action === "session.run" && !matchesRuntimeSelection(pending.model, expected.model))
|
|
809
|
+
return false;
|
|
794
810
|
if (!matchesDefined(pending.promptHash, expected.promptHash))
|
|
795
811
|
return false;
|
|
796
812
|
if (!matchesDefined(pending.runtimeSessionId, expected.runtimeSessionId))
|
|
@@ -801,6 +817,8 @@ function pendingCommandMatchesMessage(pending, expected) {
|
|
|
801
817
|
return false;
|
|
802
818
|
if (!matchesDefined(pending.targetSessionId, expected.targetSessionId))
|
|
803
819
|
return false;
|
|
820
|
+
if (expected.action === "session.run" && !matchesRuntimeSelection(pending.thoughtLevel, expected.thoughtLevel))
|
|
821
|
+
return false;
|
|
804
822
|
if (!matchesDefined(pending.turnId, expected.turnId))
|
|
805
823
|
return false;
|
|
806
824
|
if (!matchesDefined(pending.checkpoint, expected.checkpoint))
|
|
@@ -833,6 +851,8 @@ function retainedResponseMatchesCommand(previous, expected) {
|
|
|
833
851
|
return false;
|
|
834
852
|
if (!matchesDefined(responseTurnId(response), expected.turnId))
|
|
835
853
|
return false;
|
|
854
|
+
if (expected.action === "session.run" && (expected.model !== undefined || expected.thoughtLevel !== undefined))
|
|
855
|
+
return false;
|
|
836
856
|
if (expected.action === "machine.devExec" && !legacyDevExecResponseMatches(response, expected))
|
|
837
857
|
return false;
|
|
838
858
|
return true;
|
|
@@ -911,6 +931,17 @@ function responseTurnId(response) {
|
|
|
911
931
|
function matchesDefined(actual, expected) {
|
|
912
932
|
return expected === undefined || actual === expected;
|
|
913
933
|
}
|
|
934
|
+
function matchesRuntimeSelection(actual, expected) {
|
|
935
|
+
return actual === expected;
|
|
936
|
+
}
|
|
937
|
+
function normalizeRuntimeSelectionContext(value) {
|
|
938
|
+
if (value === undefined)
|
|
939
|
+
return undefined;
|
|
940
|
+
if (value === null)
|
|
941
|
+
return null;
|
|
942
|
+
const normalized = value.trim();
|
|
943
|
+
return normalized.length === 0 || normalized.toLowerCase() === "default" ? null : normalized;
|
|
944
|
+
}
|
|
914
945
|
function sendRequestIdConflict(connection, send, requestId) {
|
|
915
946
|
send(connection.socket, {
|
|
916
947
|
type: "server:error",
|
|
@@ -21,6 +21,38 @@ function primitiveSupport(value) {
|
|
|
21
21
|
function historyModel(value) {
|
|
22
22
|
return value === "append-only" || value === "authoritative-linear" ? value : undefined;
|
|
23
23
|
}
|
|
24
|
+
function runtimeSelectionOption(value) {
|
|
25
|
+
if (!isRecord(value) || typeof value.key !== "string" || value.key.length === 0 || typeof value.label !== "string" || value.label.length === 0) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
key: value.key,
|
|
30
|
+
label: value.label,
|
|
31
|
+
...(typeof value.description === "string" || value.description === null ? { description: value.description } : {}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function runtimeSelectionOptions(value) {
|
|
35
|
+
if (!Array.isArray(value))
|
|
36
|
+
return undefined;
|
|
37
|
+
const options = value.flatMap((item) => {
|
|
38
|
+
const option = runtimeSelectionOption(item);
|
|
39
|
+
return option ? [option] : [];
|
|
40
|
+
});
|
|
41
|
+
return options.length > 0 ? options : undefined;
|
|
42
|
+
}
|
|
43
|
+
function runtimeOptions(value) {
|
|
44
|
+
if (!isRecord(value))
|
|
45
|
+
return undefined;
|
|
46
|
+
const models = runtimeSelectionOptions(value.models);
|
|
47
|
+
const thoughtLevels = runtimeSelectionOptions(value.thoughtLevels);
|
|
48
|
+
const normalized = {
|
|
49
|
+
...(models ? { models } : {}),
|
|
50
|
+
...(thoughtLevels ? { thoughtLevels } : {}),
|
|
51
|
+
...(typeof value.currentModel === "string" && value.currentModel.length > 0 ? { currentModel: value.currentModel } : {}),
|
|
52
|
+
...(typeof value.currentThoughtLevel === "string" && value.currentThoughtLevel.length > 0 ? { currentThoughtLevel: value.currentThoughtLevel } : {}),
|
|
53
|
+
};
|
|
54
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
55
|
+
}
|
|
24
56
|
export function normalizeMachineCapabilities(value) {
|
|
25
57
|
const record = isRecord(value) ? value : {};
|
|
26
58
|
const agents = Array.isArray(record.agents)
|
|
@@ -32,6 +64,7 @@ export function normalizeMachineCapabilities(value) {
|
|
|
32
64
|
label: agent.label,
|
|
33
65
|
...(typeof agent.command === "string" ? { command: agent.command } : {}),
|
|
34
66
|
...(historyModel(agent.historyModel) ? { historyModel: historyModel(agent.historyModel) } : {}),
|
|
67
|
+
...(runtimeOptions(agent.runtimeOptions) ? { runtimeOptions: runtimeOptions(agent.runtimeOptions) } : {}),
|
|
35
68
|
...(isRecord(agent.sessionPrimitives)
|
|
36
69
|
? {
|
|
37
70
|
sessionPrimitives: {
|
|
@@ -285,6 +285,7 @@ export function createRelayContext(options) {
|
|
|
285
285
|
...(pending.command !== undefined ? { command: pending.command } : {}),
|
|
286
286
|
...(pending.cwd !== undefined ? { cwd: pending.cwd } : {}),
|
|
287
287
|
machineId: pending.machineId,
|
|
288
|
+
...(pending.model !== undefined ? { model: pending.model } : {}),
|
|
288
289
|
...(pending.name !== undefined ? { name: pending.name } : {}),
|
|
289
290
|
...(pending.promptHash !== undefined ? { promptHash: pending.promptHash } : {}),
|
|
290
291
|
...(pending.runtimeSessionId !== undefined ? { runtimeSessionId: pending.runtimeSessionId } : {}),
|
|
@@ -293,6 +294,7 @@ export function createRelayContext(options) {
|
|
|
293
294
|
...(pending.sourceSessionId !== undefined ? { sourceSessionId: pending.sourceSessionId } : {}),
|
|
294
295
|
...(pending.targetSessionId !== undefined ? { targetSessionId: pending.targetSessionId } : {}),
|
|
295
296
|
...(pending.timeoutMs !== undefined ? { timeoutMs: pending.timeoutMs } : {}),
|
|
297
|
+
...(pending.thoughtLevel !== undefined ? { thoughtLevel: pending.thoughtLevel } : {}),
|
|
296
298
|
...(pending.turnId !== undefined ? { turnId: pending.turnId } : {}),
|
|
297
299
|
});
|
|
298
300
|
}
|
|
@@ -319,6 +321,7 @@ export function createRelayContext(options) {
|
|
|
319
321
|
...(typeof parsed.command === "string" ? { command: parsed.command } : {}),
|
|
320
322
|
...(typeof parsed.cwd === "string" ? { cwd: parsed.cwd } : {}),
|
|
321
323
|
machineId: parsed.machineId,
|
|
324
|
+
...(runtimeSelectionContext(parsed.model) !== undefined ? { model: runtimeSelectionContext(parsed.model) } : {}),
|
|
322
325
|
...(typeof parsed.name === "string" ? { name: parsed.name } : {}),
|
|
323
326
|
...(typeof parsed.promptHash === "string" ? { promptHash: parsed.promptHash } : {}),
|
|
324
327
|
requestId: "",
|
|
@@ -328,6 +331,7 @@ export function createRelayContext(options) {
|
|
|
328
331
|
...(typeof parsed.sourceSessionId === "string" ? { sourceSessionId: parsed.sourceSessionId } : {}),
|
|
329
332
|
...(typeof parsed.targetSessionId === "string" ? { targetSessionId: parsed.targetSessionId } : {}),
|
|
330
333
|
...(typeof parsed.timeoutMs === "number" ? { timeoutMs: parsed.timeoutMs } : {}),
|
|
334
|
+
...(runtimeSelectionContext(parsed.thoughtLevel) !== undefined ? { thoughtLevel: runtimeSelectionContext(parsed.thoughtLevel) } : {}),
|
|
331
335
|
...(typeof parsed.turnId === "string" ? { turnId: parsed.turnId } : {}),
|
|
332
336
|
};
|
|
333
337
|
}
|
|
@@ -335,6 +339,11 @@ export function createRelayContext(options) {
|
|
|
335
339
|
return undefined;
|
|
336
340
|
}
|
|
337
341
|
}
|
|
342
|
+
function runtimeSelectionContext(value) {
|
|
343
|
+
if (value === null)
|
|
344
|
+
return null;
|
|
345
|
+
return typeof value === "string" ? value : undefined;
|
|
346
|
+
}
|
|
338
347
|
function rememberDirectCommandResponse(accountId, machineId, requestId, response) {
|
|
339
348
|
const pending = pendingWithMachine(accountId, machineId, requestId);
|
|
340
349
|
const recipientDeviceId = pending?.recipientDeviceId;
|