@cydm/happy-elves 0.1.0-beta.58 → 0.1.0-beta.59
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/orchestrator.d.ts +2 -1
- package/apps/cli/dist/commands/lib/orchestrator.js +3 -0
- package/apps/cli/dist/commands/lib/usage.js +28 -7
- package/apps/cli/dist/commands/machine.js +29 -5
- package/apps/cli/dist/commands/orchestrator.js +94 -42
- package/apps/cli/dist/commands/session.js +24 -0
- package/apps/cli/dist/commands/skill.js +1 -0
- package/apps/cli/dist/skills/registry.js +11 -6
- package/apps/daemon/dist/gateway/runtime.js +1 -0
- package/apps/daemon/dist/runtime/external-provider.js +3 -2
- package/apps/daemon/dist/runtime.js +1 -1
- package/apps/daemon/dist/session/lifecycle.d.ts +2 -1
- package/apps/daemon/dist/session/lifecycle.js +42 -7
- package/apps/daemon/dist/session/metadata.d.ts +2 -0
- package/apps/daemon/dist/session/metadata.js +49 -0
- package/apps/daemon/dist/session/primitives.js +23 -13
- package/apps/daemon/dist/session/prompt.js +11 -7
- package/apps/daemon/dist/types.d.ts +6 -0
- package/apps/daemon/package.json +1 -1
- package/apps/relay/dist/connections.d.ts +3 -0
- package/apps/relay/dist/controller-handlers.d.ts +2 -1
- package/apps/relay/dist/controller-handlers.js +119 -8
- package/apps/relay/dist/db.js +2 -0
- package/apps/relay/dist/http-routes.js +9 -3
- package/apps/relay/dist/http-schemas.d.ts +23 -0
- package/apps/relay/dist/http-schemas.js +2 -0
- package/apps/relay/dist/machine-command-result-handlers.js +29 -11
- package/apps/relay/dist/metadata-source.d.ts +3 -0
- package/apps/relay/dist/metadata-source.js +16 -0
- package/apps/relay/dist/projection-repository.d.ts +0 -7
- package/apps/relay/dist/projection-repository.js +0 -6
- package/apps/relay/dist/projections.js +2 -1
- package/apps/relay/dist/relay-context.d.ts +6 -20
- package/apps/relay/dist/relay-context.js +11 -1
- package/apps/relay/dist/repositories.d.ts +3 -1
- package/apps/relay/dist/repositories.js +23 -7
- package/apps/relay/dist/session-projection-reducer.js +8 -11
- package/apps/relay/dist/types.d.ts +2 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/packages/client/dist/client.d.ts +7 -2
- package/packages/client/dist/client.js +39 -4
- package/packages/client/dist/live.js +9 -1
- package/packages/client/dist/parsers.js +2 -0
- package/packages/client/dist/types.d.ts +3 -0
- package/packages/pie-provider/dist/index.js +3 -3
- package/packages/runtime/dist/index.d.ts +1 -0
- package/packages/runtime-acpx/dist/index.d.ts +2 -2
- package/packages/runtime-acpx/dist/index.js +1 -1
- package/packages/runtime-cli/dist/codex-protocol.js +1 -1
- package/packages/runtime-cli/dist/permissions.js +3 -3
- package/packages/shared/dist/protocol-schemas.d.ts +1 -0
- package/packages/shared/dist/protocol-schemas.js +1 -0
- package/packages/shared/dist/protocol-types.d.ts +1 -0
- package/packages/shared/dist/protocol.d.ts +12 -0
- package/packages/shared/dist/protocol.js +14 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SessionSnapshot } from "../../../../../packages/shared/dist/index.js";
|
|
1
|
+
import type { PermissionMode, SessionSnapshot } from "../../../../../packages/shared/dist/index.js";
|
|
2
2
|
import type { OrchestrationSummary } from "./types.js";
|
|
3
3
|
import type { TurnSummary } from "./session-output.js";
|
|
4
4
|
export type OrchestratorStatus = "idle" | "inProgress" | "completed" | "needsInput" | "blocked" | "failed" | "cancelled" | "offline" | "closed";
|
|
@@ -10,6 +10,7 @@ export type OrchestratorRequestRecord = {
|
|
|
10
10
|
runtimeTurnId?: string;
|
|
11
11
|
model?: string | null;
|
|
12
12
|
thoughtLevel?: string | null;
|
|
13
|
+
permissionMode?: PermissionMode;
|
|
13
14
|
createdAt: number;
|
|
14
15
|
};
|
|
15
16
|
export type OrchestratorMetadata = {
|
|
@@ -83,6 +83,9 @@ export function readOrchestratorMetadata(metadata) {
|
|
|
83
83
|
...(typeof candidate.runtimeTurnId === "string" ? { runtimeTurnId: candidate.runtimeTurnId } : {}),
|
|
84
84
|
...(candidate.model === null || typeof candidate.model === "string" ? { model: candidate.model } : {}),
|
|
85
85
|
...(candidate.thoughtLevel === null || typeof candidate.thoughtLevel === "string" ? { thoughtLevel: candidate.thoughtLevel } : {}),
|
|
86
|
+
...(candidate.permissionMode === "approve-all" || candidate.permissionMode === "approve-reads" || candidate.permissionMode === "deny-all"
|
|
87
|
+
? { permissionMode: candidate.permissionMode }
|
|
88
|
+
: {}),
|
|
86
89
|
createdAt: candidate.createdAt,
|
|
87
90
|
};
|
|
88
91
|
}
|
|
@@ -31,8 +31,10 @@ Check local controller config, relay reachability, and daemon state.
|
|
|
31
31
|
happy-elves remote revoke <deviceId> --json
|
|
32
32
|
`,
|
|
33
33
|
machine: `Usage:
|
|
34
|
-
happy-elves machine list [--verbose] [--json]
|
|
35
|
-
happy-elves machine status <machineId> [--verbose] [--json]
|
|
34
|
+
happy-elves machine list [--include-archived] [--verbose] [--json]
|
|
35
|
+
happy-elves machine status <machineId> [--include-archived] [--verbose] [--json]
|
|
36
|
+
happy-elves machine archive <machineId> [--json]
|
|
37
|
+
happy-elves machine unarchive <machineId> [--json]
|
|
36
38
|
happy-elves machine diagnose <machineId> [--json]
|
|
37
39
|
happy-elves machine directory <machineId> [--path <path>] [--json]
|
|
38
40
|
happy-elves machine exec <machineId> --shell <command> [--cwd <path>] [--timeout-ms <ms>] [--wait-timeout-ms <ms>] [--json]
|
|
@@ -58,6 +60,9 @@ Codex-like orchestration:
|
|
|
58
60
|
returns a deterministic turnId for the supplied request id. wait-thread waits
|
|
59
61
|
for that exact turn id instead of following the latest turn. handoff injects
|
|
60
62
|
bounded source turn context directly into the target prompt.
|
|
63
|
+
Permission defaults to trusted-local Yolo (approve-all). Use --permission-mode
|
|
64
|
+
default/ask/accept-edits/approve-reads/deny-all/read-only only to tighten one
|
|
65
|
+
turn.
|
|
61
66
|
`,
|
|
62
67
|
session: `Usage:
|
|
63
68
|
happy-elves session create --machine <machineId> [--agent codex] [--cwd <path>] [--name <name>] [--permission-mode <mode>] [--model <key>] [--thought-level <key>|--thinking <key>] --json
|
|
@@ -88,7 +93,7 @@ Session management:
|
|
|
88
93
|
happy-elves turn list <sessionId> [--limit 10] [--verbose] [--json]
|
|
89
94
|
happy-elves turn status <sessionId> <turnId> [--verbose] [--json]
|
|
90
95
|
happy-elves turn result <sessionId> [<turnId>] [--verbose] [--json]
|
|
91
|
-
happy-elves turn run <sessionId> (--text <prompt> | --text-file <path>) [--detach] [--timeout 1000s] [--runtime-timeout 1000s] [--permission-mode
|
|
96
|
+
happy-elves turn run <sessionId> (--text <prompt> | --text-file <path>) [--detach] [--timeout 1000s] [--runtime-timeout 1000s] [--permission-mode <mode>] [--model <key>] [--thought-level <key>|--thinking <key>] [--json]
|
|
92
97
|
happy-elves turn wait <sessionId> <turnId> [--timeout 1000s] --json
|
|
93
98
|
happy-elves turn cancel <sessionId> <turnId> [--reason <reason>] --json
|
|
94
99
|
happy-elves turn rewind <sessionId> <turn-or-checkpoint-id> --json
|
|
@@ -96,7 +101,9 @@ Session management:
|
|
|
96
101
|
|
|
97
102
|
Turn management:
|
|
98
103
|
run starts one agent execution. It waits by default; use --detach for
|
|
99
|
-
background execution.
|
|
104
|
+
background execution. Permission defaults to trusted-local Yolo (approve-all);
|
|
105
|
+
pass --permission-mode default/ask/accept-edits/approve-reads/deny-all/read-only
|
|
106
|
+
to tighten.
|
|
100
107
|
wait/result inspect a specific turn.
|
|
101
108
|
rewind/fork operate on a turn or checkpoint id supplied by runtime history.
|
|
102
109
|
`,
|
|
@@ -164,7 +171,7 @@ export function commandUsage(command, exitCode = 1) {
|
|
|
164
171
|
export function usageText() {
|
|
165
172
|
return `happy-elves
|
|
166
173
|
|
|
167
|
-
Happy Elves is a remote controller and orchestration layer for coding agents across machines. The local agent remains the authority for runtime state. Agents should run happy-elves
|
|
174
|
+
Happy Elves is a remote controller and orchestration layer for coding agents across machines. The local agent remains the authority for runtime state. Agents should run happy-elves skill doctor --name happy-elves-orchestrator --json before orchestrating; happy-elves --skill remains legacy guidance.
|
|
168
175
|
|
|
169
176
|
Core commands:
|
|
170
177
|
machine list [--json]
|
|
@@ -248,6 +255,19 @@ Recommended agent workflow:
|
|
|
248
255
|
5. orchestrator read-turn <threadId> <turnId> --json
|
|
249
256
|
6. orchestrator handoff --from <sourceThreadId> --turn <sourceTurnId> --to <targetThreadId> --text <instruction> --request-id <stable-id> --json
|
|
250
257
|
|
|
258
|
+
Permission defaults:
|
|
259
|
+
Happy Elves is trusted-local by default. If orchestrator send-message omits
|
|
260
|
+
--permission-mode, the effective permission is approve-all. Pass
|
|
261
|
+
--permission-mode default/ask/accept-edits/approve-reads/deny-all/read-only
|
|
262
|
+
only when you intentionally want to tighten a turn. Inspect
|
|
263
|
+
effectivePermissionMode and permissionSource before diagnosing sandbox, git,
|
|
264
|
+
desktop, or backend permission failures.
|
|
265
|
+
|
|
266
|
+
Orchestrator idempotency:
|
|
267
|
+
Reusing the same request id with the same prompt/model/thinking/permission
|
|
268
|
+
returns the same turn. Reusing it with different prompt/model/thinking/
|
|
269
|
+
permission returns REQUEST_ID_CONFLICT.
|
|
270
|
+
|
|
251
271
|
Concepts:
|
|
252
272
|
Loaded session: a Happy Elves control-plane session that can be operated by
|
|
253
273
|
this CLI.
|
|
@@ -282,8 +302,9 @@ Output modes:
|
|
|
282
302
|
thread's history is unavailable, the item reports historyStatus/historyError
|
|
283
303
|
instead of pretending to be healthy inProgress.
|
|
284
304
|
send-message requires --request-id and is detached by default. Reusing the
|
|
285
|
-
same request id with the same prompt/model/thinking returns the
|
|
286
|
-
reusing it with different prompt/model/thinking returns
|
|
305
|
+
same request id with the same prompt/model/thinking/permission returns the
|
|
306
|
+
same turn; reusing it with different prompt/model/thinking/permission returns
|
|
307
|
+
REQUEST_ID_CONFLICT.
|
|
287
308
|
After send-message, keep the orchestrator turn open and wait on the exact
|
|
288
309
|
returned turn id until terminal. Do not stop after dispatching the prompt.
|
|
289
310
|
For direct handoff, prefer handoff or send-message --context-turn. The CLI
|
|
@@ -6,6 +6,8 @@ function structuredMachineSummary(machine) {
|
|
|
6
6
|
machineId: machine.id,
|
|
7
7
|
name: machine.metadata.name ?? machine.name,
|
|
8
8
|
online: machine.online,
|
|
9
|
+
archived: Boolean(machine.archivedAt),
|
|
10
|
+
archivedAt: machine.archivedAt,
|
|
9
11
|
sessionCount: summary.sessionCount,
|
|
10
12
|
runningCount: summary.runningSessionCount,
|
|
11
13
|
blockedCount: summary.blockedSessionCount,
|
|
@@ -15,6 +17,8 @@ function structuredMachineVerbose(machine) {
|
|
|
15
17
|
return {
|
|
16
18
|
machineId: machine.id,
|
|
17
19
|
online: machine.online,
|
|
20
|
+
archived: Boolean(machine.archivedAt),
|
|
21
|
+
archivedAt: machine.archivedAt,
|
|
18
22
|
lastSeen: machine.lastSeen,
|
|
19
23
|
name: machine.metadata.name ?? machine.name,
|
|
20
24
|
defaultCwd: machine.defaultCwd,
|
|
@@ -28,14 +32,17 @@ function structuredMachineVerbose(machine) {
|
|
|
28
32
|
function printMachineLine(machine) {
|
|
29
33
|
const name = machine.metadata.name ?? machine.name;
|
|
30
34
|
const summary = machine.summary;
|
|
31
|
-
|
|
35
|
+
const state = machine.archivedAt ? "archived" : machine.online ? "online" : "offline";
|
|
36
|
+
console.log(`${machine.id} ${state} ${compactText(name, 32)} sessions:${summary.sessionCount} running:${summary.runningSessionCount} blocked:${summary.blockedSessionCount}`);
|
|
32
37
|
}
|
|
33
38
|
function printMachineDetails(machine) {
|
|
34
39
|
const name = machine.metadata.name ?? machine.name;
|
|
35
40
|
const agents = machine.capabilities.agents.map((agent) => agent.id).join(", ") || "-";
|
|
36
41
|
const summary = machine.summary;
|
|
37
42
|
console.log(`${name} (${machine.id})`);
|
|
38
|
-
console.log(` status: ${machine.online ? "online" : "offline"} agents: ${agents}`);
|
|
43
|
+
console.log(` status: ${machine.archivedAt ? "archived" : machine.online ? "online" : "offline"} agents: ${agents}`);
|
|
44
|
+
if (machine.archivedAt)
|
|
45
|
+
console.log(` archived: ${new Date(machine.archivedAt).toISOString()}`);
|
|
39
46
|
console.log(` cwd: ${machine.defaultCwd ?? "-"}`);
|
|
40
47
|
console.log(` sessions: ${summary.sessionCount} total, ${summary.runningSessionCount} running, ${summary.readySessionCount} ready, ${summary.needsUserSessionCount} needs user`);
|
|
41
48
|
console.log(` sessions: happy-elves session list --machine ${machine.id} --limit 20`);
|
|
@@ -171,7 +178,7 @@ export async function handleMachine({ domain, action, positional, flags }) {
|
|
|
171
178
|
return true;
|
|
172
179
|
}
|
|
173
180
|
if (action === "list") {
|
|
174
|
-
const snapshot = await client.snapshot();
|
|
181
|
+
const snapshot = await client.snapshot({ includeArchived: flags["include-archived"] === true });
|
|
175
182
|
const machines = await Promise.all(snapshot.machines.map(async (machine) => ({
|
|
176
183
|
...(await showMachine(client, machine)),
|
|
177
184
|
...(await machineSessionOverview(client, snapshot.sessions, machine.id)),
|
|
@@ -192,9 +199,26 @@ export async function handleMachine({ domain, action, positional, flags }) {
|
|
|
192
199
|
});
|
|
193
200
|
return true;
|
|
194
201
|
}
|
|
202
|
+
if (action === "archive" || action === "unarchive" || action === "restore") {
|
|
203
|
+
const machineId = requirePositional(positional[0], "machineId");
|
|
204
|
+
const machine = action === "archive"
|
|
205
|
+
? await client.archiveMachine(machineId)
|
|
206
|
+
: await client.unarchiveMachine(machineId);
|
|
207
|
+
const projectedMachine = await showMachine(client, machine);
|
|
208
|
+
const overviewSnapshot = await client.snapshot({ includeArchived: true });
|
|
209
|
+
const overview = await machineSessionOverview(client, overviewSnapshot.sessions, machine.id);
|
|
210
|
+
if (!wantsJson(flags)) {
|
|
211
|
+
printMachineLine({ ...projectedMachine, ...overview });
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
ok(action === "archive" ? "machine.archive" : "machine.unarchive", {
|
|
215
|
+
machine: structuredMachineVerbose({ ...projectedMachine, ...overview }),
|
|
216
|
+
}, { machineId: machine.id });
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
195
219
|
if (action === "status") {
|
|
196
220
|
const machineId = requirePositional(positional[0], "machineId");
|
|
197
|
-
const snapshot = await client.snapshot();
|
|
221
|
+
const snapshot = await client.snapshot({ includeArchived: flags["include-archived"] === true });
|
|
198
222
|
const machine = snapshot.machines.find((item) => item.id === machineId);
|
|
199
223
|
if (!machine)
|
|
200
224
|
throw new CliError("Machine not found", "MACHINE_NOT_FOUND");
|
|
@@ -212,7 +236,7 @@ export async function handleMachine({ domain, action, positional, flags }) {
|
|
|
212
236
|
machineId: machine.id,
|
|
213
237
|
name: projectedMachine.metadata.name ?? machine.name,
|
|
214
238
|
online: machine.online,
|
|
215
|
-
state: machine.online ? "available" : "offline",
|
|
239
|
+
state: machine.archivedAt ? "archived" : machine.online ? "available" : "offline",
|
|
216
240
|
summary: overview.summary,
|
|
217
241
|
}),
|
|
218
242
|
}, { machineId: machine.id });
|
|
@@ -105,7 +105,34 @@ async function readThreadContext(client, threadId) {
|
|
|
105
105
|
}
|
|
106
106
|
return { machines: snapshot.machines, session };
|
|
107
107
|
}
|
|
108
|
-
function
|
|
108
|
+
function effectivePermissionMode(requestedPermissionMode) {
|
|
109
|
+
return requestedPermissionMode ?? "approve-all";
|
|
110
|
+
}
|
|
111
|
+
function permissionEvidence(requestedPermissionMode, record) {
|
|
112
|
+
if (record?.permissionMode) {
|
|
113
|
+
return {
|
|
114
|
+
effectivePermissionMode: record.permissionMode,
|
|
115
|
+
permissionSource: "stored-request",
|
|
116
|
+
...(requestedPermissionMode !== undefined ? { requestedPermissionMode } : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (record) {
|
|
120
|
+
return {
|
|
121
|
+
permissionSource: "legacy-record",
|
|
122
|
+
...(requestedPermissionMode !== undefined ? { requestedPermissionMode } : {}),
|
|
123
|
+
permissionWarning: "existing turn predates stored permission evidence",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
effectivePermissionMode: effectivePermissionMode(requestedPermissionMode),
|
|
128
|
+
permissionSource: requestedPermissionMode === undefined ? "product-default" : "request",
|
|
129
|
+
...(requestedPermissionMode !== undefined ? { requestedPermissionMode } : {}),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function samePermissionShape(existing, permissionMode) {
|
|
133
|
+
return existing.permissionMode === undefined || existing.permissionMode === permissionMode;
|
|
134
|
+
}
|
|
135
|
+
function requestRecordForExistingTurn(turn, hash, selection, permissionMode, inputSha256, sourceOutputSha256, createdAt) {
|
|
109
136
|
if (!turn)
|
|
110
137
|
return undefined;
|
|
111
138
|
return {
|
|
@@ -115,6 +142,7 @@ function requestRecordForExistingTurn(turn, hash, selection, inputSha256, source
|
|
|
115
142
|
...(sourceOutputSha256 ? { sourceOutputSha256 } : {}),
|
|
116
143
|
...(turn.runtimeTurnId ? { runtimeTurnId: turn.runtimeTurnId } : {}),
|
|
117
144
|
...selectionRecordFields(selection),
|
|
145
|
+
...(permissionMode !== undefined ? { permissionMode } : {}),
|
|
118
146
|
createdAt: turn.startedAt ?? turn.updatedAt ?? createdAt,
|
|
119
147
|
};
|
|
120
148
|
}
|
|
@@ -130,13 +158,15 @@ function sameRequestRecord(existing, expected) {
|
|
|
130
158
|
existing.inputSha256 === expected.inputSha256 &&
|
|
131
159
|
existing.sourceOutputSha256 === expected.sourceOutputSha256 &&
|
|
132
160
|
existing.model === expected.model &&
|
|
133
|
-
existing.thoughtLevel === expected.thoughtLevel
|
|
161
|
+
existing.thoughtLevel === expected.thoughtLevel &&
|
|
162
|
+
samePermissionShape(existing, expected.permissionMode ?? "approve-all");
|
|
134
163
|
}
|
|
135
164
|
function sameBaseRequestShape(existing, input) {
|
|
136
165
|
return existing.turnId === input.turnId &&
|
|
137
166
|
existing.inputSha256 === input.inputSha256 &&
|
|
138
167
|
existing.model === input.model &&
|
|
139
|
-
existing.thoughtLevel === input.thoughtLevel
|
|
168
|
+
existing.thoughtLevel === input.thoughtLevel &&
|
|
169
|
+
samePermissionShape(existing, input.permissionMode);
|
|
140
170
|
}
|
|
141
171
|
function sameEffectiveRequestShape(existing, input) {
|
|
142
172
|
return existing.promptSha256 === input.promptSha256 &&
|
|
@@ -383,25 +413,26 @@ async function safeEventsForThreadSummary(client, session) {
|
|
|
383
413
|
return { events: [], historyFailure: threadHistoryFailure(error) };
|
|
384
414
|
}
|
|
385
415
|
}
|
|
386
|
-
async function
|
|
416
|
+
async function persistRequestRecord(client, threadId, requestId, record) {
|
|
387
417
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
388
418
|
const { session } = await readThreadContext(client, threadId);
|
|
389
419
|
const metadata = await client.decodeSessionMetadata(session);
|
|
390
420
|
const existing = readOrchestratorMetadata(metadata).requests?.[requestId];
|
|
391
421
|
if (existing) {
|
|
392
422
|
if (sameRequestRecord(existing, record))
|
|
393
|
-
return;
|
|
394
|
-
|
|
423
|
+
return { metadata, session };
|
|
424
|
+
throw requestConflict(threadId, requestId, record.turnId);
|
|
395
425
|
}
|
|
396
426
|
try {
|
|
397
|
-
await client.updateSessionMetadata(session.id, withOrchestratorRequest(metadata, requestId, record), { expectedUpdatedAt: session.updatedAt });
|
|
398
|
-
return;
|
|
427
|
+
const updated = await client.updateSessionMetadata(session.id, withOrchestratorRequest(metadata, requestId, record), { expectedEncryptedMetadata: session.encryptedMetadata ?? null, expectedUpdatedAt: session.updatedAt });
|
|
428
|
+
return { metadata: await client.decodeSessionMetadata(updated), session: updated };
|
|
399
429
|
}
|
|
400
430
|
catch (error) {
|
|
401
431
|
if (errorCode(error) !== "SESSION_METADATA_CONFLICT")
|
|
402
|
-
|
|
432
|
+
throw error;
|
|
403
433
|
}
|
|
404
434
|
}
|
|
435
|
+
throw new OrchestratorError("Session metadata changed while recording request id; retry with the latest session.", "SESSION_METADATA_CONFLICT", { retryable: true, sessionId: threadId, turnId: record.turnId });
|
|
405
436
|
}
|
|
406
437
|
function terminalStatusForTurn(turn) {
|
|
407
438
|
if (turn.status === "completed")
|
|
@@ -413,7 +444,7 @@ function terminalStatusForTurn(turn) {
|
|
|
413
444
|
return "inProgress";
|
|
414
445
|
}
|
|
415
446
|
function requestConflict(threadId, requestId, turnId) {
|
|
416
|
-
return new OrchestratorError(`Request id already exists with different prompt, model, or
|
|
447
|
+
return new OrchestratorError(`Request id already exists with different prompt, model, thoughtLevel, or permissionMode: ${requestId}`, "REQUEST_ID_CONFLICT", { retryable: false, sessionId: threadId, turnId });
|
|
417
448
|
}
|
|
418
449
|
function threadBusy(threadId, active, latestCompleted) {
|
|
419
450
|
return new OrchestratorError(`Thread already has a running turn: ${active.turnId}`, "THREAD_BUSY", {
|
|
@@ -477,6 +508,8 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
477
508
|
const requestId = requireString(flags, "request-id");
|
|
478
509
|
const turnId = deterministicTurnId(input.threadId, requestId);
|
|
479
510
|
const relayRequestId = deterministicRelayRequestId(input.threadId, requestId);
|
|
511
|
+
const requestedPermissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
512
|
+
const activePermissionMode = effectivePermissionMode(requestedPermissionMode);
|
|
480
513
|
const model = parseRuntimeSelection(flags.model, "model");
|
|
481
514
|
const thoughtLevel = parseThoughtLevel(flags);
|
|
482
515
|
const selection = selectionRecordFields({ model, thoughtLevel });
|
|
@@ -495,6 +528,8 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
495
528
|
const { session } = await readThreadContext(client, input.threadId);
|
|
496
529
|
const metadata = await client.decodeSessionMetadata(session);
|
|
497
530
|
const existingRequest = readOrchestratorMetadata(metadata).requests?.[requestId];
|
|
531
|
+
let existingRequestContext;
|
|
532
|
+
let existingRequestText;
|
|
498
533
|
const earlyContextData = input.contextTurn ? {
|
|
499
534
|
context: {
|
|
500
535
|
sourceThreadId: input.contextTurn.sourceThreadId,
|
|
@@ -503,7 +538,7 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
503
538
|
},
|
|
504
539
|
} : {};
|
|
505
540
|
if (existingRequest) {
|
|
506
|
-
if (!sameBaseRequestShape(existingRequest, { turnId, inputSha256, ...selection }))
|
|
541
|
+
if (!sameBaseRequestShape(existingRequest, { turnId, inputSha256, ...selection, permissionMode: activePermissionMode }))
|
|
507
542
|
throw requestConflict(input.threadId, requestId, turnId);
|
|
508
543
|
const existingTurn = await readExactTurn(client, input.threadId, turnId, metadata);
|
|
509
544
|
let context;
|
|
@@ -517,8 +552,22 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
517
552
|
resolvedEffectivePrompt = false;
|
|
518
553
|
}
|
|
519
554
|
}
|
|
555
|
+
if (!resolvedEffectivePrompt) {
|
|
556
|
+
ok(input.responseType, {
|
|
557
|
+
threadId: input.threadId,
|
|
558
|
+
sessionId: input.threadId,
|
|
559
|
+
turnId,
|
|
560
|
+
requestId,
|
|
561
|
+
status: "inProgress",
|
|
562
|
+
rawStatus: existingTurn?.status ?? "pending",
|
|
563
|
+
...permissionEvidence(requestedPermissionMode, existingRequest),
|
|
564
|
+
...earlyContextData,
|
|
565
|
+
}, { requestId, sessionId: input.threadId, turnId });
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
520
568
|
if (resolvedEffectivePrompt) {
|
|
521
569
|
const effectivePrompt = context?.prompt ?? input.instruction;
|
|
570
|
+
existingRequestText = effectivePrompt;
|
|
522
571
|
if (!sameEffectiveRequestShape(existingRequest, {
|
|
523
572
|
promptSha256: promptSha256(effectivePrompt),
|
|
524
573
|
...(context?.sourceOutputSha256 ? { sourceOutputSha256: context.sourceOutputSha256 } : {}),
|
|
@@ -541,6 +590,7 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
541
590
|
latest: output.latestPath,
|
|
542
591
|
metadata: output.metadataPath,
|
|
543
592
|
},
|
|
593
|
+
...permissionEvidence(requestedPermissionMode, existingRequest),
|
|
544
594
|
...(context ? {
|
|
545
595
|
context: {
|
|
546
596
|
sourceThreadId: context.source.threadId,
|
|
@@ -554,21 +604,25 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
554
604
|
}, { requestId, sessionId: input.threadId, turnId });
|
|
555
605
|
return;
|
|
556
606
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
607
|
+
if (existingTurn?.status === "running") {
|
|
608
|
+
ok(input.responseType, {
|
|
609
|
+
threadId: input.threadId,
|
|
610
|
+
sessionId: input.threadId,
|
|
611
|
+
turnId,
|
|
612
|
+
requestId,
|
|
613
|
+
status: "inProgress",
|
|
614
|
+
rawStatus: existingTurn.status,
|
|
615
|
+
...permissionEvidence(requestedPermissionMode, existingRequest),
|
|
616
|
+
...earlyContextData,
|
|
617
|
+
}, { requestId, sessionId: input.threadId, turnId });
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
existingRequestContext = context;
|
|
567
621
|
}
|
|
568
|
-
const context = input.contextTurn
|
|
622
|
+
const context = existingRequestContext ?? (input.contextTurn
|
|
569
623
|
? await buildContextPrompt(client, input.contextTurn, input.instruction, includeContextOutput)
|
|
570
|
-
: undefined;
|
|
571
|
-
const text = context?.prompt ?? input.instruction;
|
|
624
|
+
: undefined);
|
|
625
|
+
const text = existingRequestText ?? context?.prompt ?? input.instruction;
|
|
572
626
|
const hash = promptSha256(text);
|
|
573
627
|
const record = {
|
|
574
628
|
turnId,
|
|
@@ -576,6 +630,7 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
576
630
|
inputSha256,
|
|
577
631
|
...(context?.sourceOutputSha256 ? { sourceOutputSha256: context.sourceOutputSha256 } : {}),
|
|
578
632
|
...selection,
|
|
633
|
+
permissionMode: activePermissionMode,
|
|
579
634
|
createdAt: Date.now(),
|
|
580
635
|
};
|
|
581
636
|
const contextData = context ? {
|
|
@@ -593,32 +648,21 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
593
648
|
const existingTurn = await readExactTurn(client, input.threadId, turnId, metadata);
|
|
594
649
|
if (existingTurn?.prompt && promptSha256(existingTurn.prompt) !== hash)
|
|
595
650
|
throw requestConflict(input.threadId, requestId, turnId);
|
|
596
|
-
if (existingRequest && !existingTurn) {
|
|
597
|
-
ok(input.responseType, {
|
|
598
|
-
threadId: input.threadId,
|
|
599
|
-
sessionId: input.threadId,
|
|
600
|
-
turnId,
|
|
601
|
-
requestId,
|
|
602
|
-
status: "inProgress",
|
|
603
|
-
rawStatus: "pending",
|
|
604
|
-
...contextData,
|
|
605
|
-
}, { requestId, sessionId: input.threadId, turnId });
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
651
|
const active = turns[0]?.status === "running" ? turns[0] : undefined;
|
|
609
652
|
const latestCompleted = turns.find((turn) => turn.status === "completed");
|
|
610
653
|
if (active && active.turnId !== turnId)
|
|
611
654
|
throw threadBusy(input.threadId, active, latestCompleted);
|
|
612
655
|
if (existingTurn && existingTurn.status !== "running") {
|
|
613
656
|
const output = await rebuildOutputForTurn(client, input.threadId, existingTurn);
|
|
614
|
-
|
|
657
|
+
const existingTurnRecord = requestRecordForExistingTurn(existingTurn, hash, selection, undefined, inputSha256, context?.sourceOutputSha256, Date.now()) ?? {
|
|
615
658
|
turnId,
|
|
616
659
|
promptSha256: hash,
|
|
617
660
|
inputSha256,
|
|
618
661
|
...(context?.sourceOutputSha256 ? { sourceOutputSha256: context.sourceOutputSha256 } : {}),
|
|
619
662
|
...selection,
|
|
620
663
|
createdAt: Date.now(),
|
|
621
|
-
}
|
|
664
|
+
};
|
|
665
|
+
await persistRequestRecord(client, input.threadId, requestId, existingTurnRecord);
|
|
622
666
|
ok(input.responseType, {
|
|
623
667
|
threadId: input.threadId,
|
|
624
668
|
sessionId: input.threadId,
|
|
@@ -632,11 +676,13 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
632
676
|
latest: output.latestPath,
|
|
633
677
|
metadata: output.metadataPath,
|
|
634
678
|
},
|
|
679
|
+
...permissionEvidence(requestedPermissionMode, existingTurnRecord),
|
|
635
680
|
...contextData,
|
|
636
681
|
}, { requestId, sessionId: input.threadId, turnId });
|
|
637
682
|
return;
|
|
638
683
|
}
|
|
639
684
|
if (existingTurn?.status === "running") {
|
|
685
|
+
const runningTurnRecord = requestRecordForExistingTurn(existingTurn, hash, selection, undefined, inputSha256, context?.sourceOutputSha256, Date.now());
|
|
640
686
|
ok(input.responseType, {
|
|
641
687
|
threadId: input.threadId,
|
|
642
688
|
sessionId: input.threadId,
|
|
@@ -644,16 +690,19 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
644
690
|
requestId,
|
|
645
691
|
status: "inProgress",
|
|
646
692
|
rawStatus: existingTurn?.status ?? "running",
|
|
693
|
+
...permissionEvidence(requestedPermissionMode, runningTurnRecord),
|
|
647
694
|
...contextData,
|
|
648
695
|
}, { requestId, sessionId: input.threadId, turnId });
|
|
649
696
|
return;
|
|
650
697
|
}
|
|
651
|
-
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
652
698
|
const runtimeTimeoutMs = parseDurationMs(flags["runtime-timeout"], DEFAULT_COMMAND_TIMEOUT_MS);
|
|
699
|
+
const storedRecord = existingRequest ?? record;
|
|
700
|
+
if (!existingRequest)
|
|
701
|
+
await persistRequestRecord(client, input.threadId, requestId, record);
|
|
653
702
|
try {
|
|
654
703
|
await client.run(input.threadId, text, {
|
|
655
704
|
wait: false,
|
|
656
|
-
permissionMode,
|
|
705
|
+
permissionMode: requestedPermissionMode,
|
|
657
706
|
requestId: relayRequestId,
|
|
658
707
|
turnId,
|
|
659
708
|
promptHash: hash,
|
|
@@ -679,7 +728,6 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
679
728
|
}
|
|
680
729
|
throw error;
|
|
681
730
|
}
|
|
682
|
-
await persistRequestRecordBestEffort(client, input.threadId, requestId, record);
|
|
683
731
|
ok(input.responseType, {
|
|
684
732
|
threadId: input.threadId,
|
|
685
733
|
sessionId: input.threadId,
|
|
@@ -687,6 +735,7 @@ async function sendOrchestratorMessage(client, flags, input) {
|
|
|
687
735
|
requestId,
|
|
688
736
|
status: "inProgress",
|
|
689
737
|
rawStatus: "running",
|
|
738
|
+
...permissionEvidence(requestedPermissionMode, storedRecord),
|
|
690
739
|
...contextData,
|
|
691
740
|
}, { requestId, sessionId: input.threadId, turnId });
|
|
692
741
|
}
|
|
@@ -835,7 +884,10 @@ export async function handleOrchestrator({ domain, action, positional, flags })
|
|
|
835
884
|
const metadata = await client.decodeSessionMetadata(session);
|
|
836
885
|
let updated;
|
|
837
886
|
try {
|
|
838
|
-
updated = await client.updateSessionMetadata(threadId, withOrchestratorTags(metadata, tags), {
|
|
887
|
+
updated = await client.updateSessionMetadata(threadId, withOrchestratorTags(metadata, tags), {
|
|
888
|
+
expectedEncryptedMetadata: session.encryptedMetadata ?? null,
|
|
889
|
+
expectedUpdatedAt: session.updatedAt,
|
|
890
|
+
});
|
|
839
891
|
}
|
|
840
892
|
catch (error) {
|
|
841
893
|
if (errorCode(error) !== "SESSION_METADATA_CONFLICT")
|
|
@@ -188,12 +188,27 @@ function numberMetadata(metadata, key) {
|
|
|
188
188
|
function isRecord(value) {
|
|
189
189
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
190
190
|
}
|
|
191
|
+
function diagnosticPermission(metadata) {
|
|
192
|
+
const metadataPermissionMode = stringMetadata(metadata, "permissionMode");
|
|
193
|
+
if (metadataPermissionMode === "approve-all" || metadataPermissionMode === "approve-reads" || metadataPermissionMode === "deny-all") {
|
|
194
|
+
return {
|
|
195
|
+
effectivePermissionMode: metadataPermissionMode,
|
|
196
|
+
permissionSource: "session-metadata",
|
|
197
|
+
metadataPermissionMode,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
effectivePermissionMode: "approve-all",
|
|
202
|
+
permissionSource: "product-default",
|
|
203
|
+
};
|
|
204
|
+
}
|
|
191
205
|
function diagnosticSessionMetadata(metadata) {
|
|
192
206
|
const runtime = isRecord(metadata.runtime) ? metadata.runtime : {};
|
|
193
207
|
return {
|
|
194
208
|
agent: stringMetadata(metadata, "agent"),
|
|
195
209
|
cwd: stringMetadata(metadata, "cwd"),
|
|
196
210
|
name: stringMetadata(metadata, "name"),
|
|
211
|
+
permissionMode: stringMetadata(metadata, "permissionMode"),
|
|
197
212
|
providerName: stringMetadata(metadata, "providerName"),
|
|
198
213
|
importedFromRuntimeSessionId: stringMetadata(metadata, "importedFromRuntimeSessionId"),
|
|
199
214
|
runtime: {
|
|
@@ -510,6 +525,7 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
510
525
|
: undefined;
|
|
511
526
|
if (repair)
|
|
512
527
|
session = repair.session;
|
|
528
|
+
const permission = diagnosticPermission(metadata);
|
|
513
529
|
const data = {
|
|
514
530
|
generatedAt: new Date().toISOString(),
|
|
515
531
|
session: {
|
|
@@ -557,6 +573,9 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
557
573
|
},
|
|
558
574
|
orchestration,
|
|
559
575
|
diagnosis: {
|
|
576
|
+
effectivePermissionMode: permission.effectivePermissionMode,
|
|
577
|
+
permissionSource: permission.permissionSource,
|
|
578
|
+
metadataPermissionMode: permission.metadataPermissionMode,
|
|
560
579
|
importedRuntimeSessionId: stringMetadata(metadata, "importedFromRuntimeSessionId"),
|
|
561
580
|
latestPageEmpty: latestPage.events.length === 0,
|
|
562
581
|
latestPageHasVisibleTranscript: latestPage.events.some(isVisibleTranscriptPayloadEvent),
|
|
@@ -598,6 +617,7 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
598
617
|
console.log(`Machine: ${session.machineId}${machine ? ` ${machine.online ? "online" : "offline"}` : ""}`);
|
|
599
618
|
const runtime = isRecord(metadata.runtime) ? metadata.runtime : {};
|
|
600
619
|
console.log(`Runtime: ${stringMetadata(metadata, "importedFromRuntimeSessionId") ?? stringMetadata(runtime, "backendSessionId") ?? stringMetadata(runtime, "agentSessionId") ?? "-"}`);
|
|
620
|
+
console.log(`Permission: ${data.diagnosis.effectivePermissionMode} source=${data.diagnosis.permissionSource}`);
|
|
601
621
|
console.log(`Backfill: ${data.diagnosis.backfillStatus ?? "-"} available=${data.diagnosis.backfillAvailableEvents ?? "-"} emitted=${data.diagnosis.backfillEmittedEvents ?? "-"}`);
|
|
602
622
|
console.log(`Head: ${session.currentHead ?? "-"} lastTurn=${session.lastTurnId ?? "-"} stale=${data.diagnosis.staleHeadSuspected ? "yes" : "no"}`);
|
|
603
623
|
console.log(`Latest page: ${latestPage.events.length} events, visible=${latestPage.events.filter(isVisibleTranscriptPayloadEvent).length}, hasMore=${latestPage.hasMoreBefore}`);
|
|
@@ -618,6 +638,7 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
618
638
|
throw new CliError("Cannot repair binding for a running session", "SESSION_BINDING_REPAIR_RUNNING");
|
|
619
639
|
const metadata = await client.decodeSessionMetadata(session);
|
|
620
640
|
const { candidate, candidates } = await resolveRuntimeBindingRepairTarget(client, session, metadata, flags);
|
|
641
|
+
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
621
642
|
const name = typeof flags.name === "string" && flags.name.trim()
|
|
622
643
|
? flags.name.trim()
|
|
623
644
|
: stringMetadata(metadata, "name") ?? session.name ?? candidate.name ?? candidate.summary ?? candidate.runtimeSessionId;
|
|
@@ -627,6 +648,7 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
627
648
|
cwd: candidate.cwd,
|
|
628
649
|
runtimeSessionId: candidate.runtimeSessionId,
|
|
629
650
|
name,
|
|
651
|
+
permissionMode,
|
|
630
652
|
summary: candidate.summary,
|
|
631
653
|
runtimeState: candidate.runtimeState,
|
|
632
654
|
...(candidate.name ? { providerName: candidate.name } : {}),
|
|
@@ -729,12 +751,14 @@ export async function handleSession({ domain, action, positional, flags }) {
|
|
|
729
751
|
const name = typeof flags.name === "string" && flags.name.trim()
|
|
730
752
|
? flags.name.trim()
|
|
731
753
|
: historical.name?.trim() || historical.summary?.trim() || historical.runtimeSessionName?.trim() || runtimeSessionId;
|
|
754
|
+
const permissionMode = parsePermissionMode(flags["permission-mode"] ?? flags.permission);
|
|
732
755
|
const continued = await client.importHistoricalSession({
|
|
733
756
|
machineId,
|
|
734
757
|
agent: historical.agent,
|
|
735
758
|
cwd: historical.cwd,
|
|
736
759
|
runtimeSessionId,
|
|
737
760
|
name,
|
|
761
|
+
permissionMode,
|
|
738
762
|
summary: historical.summary,
|
|
739
763
|
runtimeState: historical.runtimeState,
|
|
740
764
|
...(historical.name ? { providerName: historical.name } : {}),
|
|
@@ -53,6 +53,7 @@ async function cliVersion() {
|
|
|
53
53
|
async function addCliEnvironmentChecks(checks, warnings) {
|
|
54
54
|
const version = await cliVersion();
|
|
55
55
|
checks.push({ name: "cli-version", status: "ok", detail: version });
|
|
56
|
+
checks.push({ name: "default-permission-mode", status: "ok", detail: "approve-all (trusted-local Yolo)" });
|
|
56
57
|
const executable = process.argv[1] ? path.resolve(process.argv[1]) : process.execPath;
|
|
57
58
|
const workspaceCli = path.resolve(process.cwd(), "apps", "cli", "dist", "index.js");
|
|
58
59
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const ORCHESTRATOR_SKILL_NAME = "happy-elves-orchestrator";
|
|
2
2
|
const COMPUTER_USE_SKILL_NAME = "happy-elves-computer-use";
|
|
3
|
-
const ORCHESTRATOR_SKILL_CONTRACT_VERSION =
|
|
4
|
-
const COMPUTER_USE_SKILL_CONTRACT_VERSION =
|
|
3
|
+
const ORCHESTRATOR_SKILL_CONTRACT_VERSION = 3;
|
|
4
|
+
const COMPUTER_USE_SKILL_CONTRACT_VERSION = 3;
|
|
5
5
|
function renderOrchestratorSkill(input) {
|
|
6
6
|
return `---
|
|
7
7
|
name: ${input.skillName}
|
|
@@ -32,8 +32,10 @@ Important rules:
|
|
|
32
32
|
- First run \`happy-elves skill doctor --name happy-elves-orchestrator --json\` and verify the \`cli-executable\`, \`cli-version\`, \`workspace-cli\`, and \`git-commit-capability\` checks. If git commit capability is warning or a commit fails, report the blocker; do not claim staged-only changes are complete.
|
|
33
33
|
- \`threadId\` is the same value as Happy Elves \`sessionId\`.
|
|
34
34
|
- Use stable request ids such as \`orch_round_3_worker_fix\`.
|
|
35
|
-
- Reusing the same request id with the same prompt/model/thinking returns the same turn.
|
|
36
|
-
- Reusing the same request id with different prompt text, model, or
|
|
35
|
+
- Reusing the same request id with the same prompt/model/thinking/permission returns the same turn.
|
|
36
|
+
- Reusing the same request id with different prompt text, model, thinking, or permission returns \`REQUEST_ID_CONFLICT\`.
|
|
37
|
+
- Happy Elves defaults to trusted-local YOLO execution: if \`send-message\` omits \`--permission-mode\`, the effective permission is \`approve-all\`. Pass \`--permission-mode default\`, \`ask\`, \`accept-edits\`, \`approve-reads\`, \`deny-all\`, or \`read-only\` only when you intentionally want to tighten one turn.
|
|
38
|
+
- Read \`effectivePermissionMode\` and \`permissionSource\` in \`send-message\` / \`handoff\` JSON before diagnosing sandbox, git, desktop, or backend permission failures.
|
|
37
39
|
- To override runtime selection, pass \`--model <key>\` and \`--thought-level <key>\`; \`--thinking <key>\` is an alias. Use \`default\` to return to provider defaults.
|
|
38
40
|
- Runtime failures such as \`CODEX_EMPTY_COMPLETION\` are terminal failed turns; inspect \`wait-thread --turn\` or \`turn result\` for the full error text.
|
|
39
41
|
- If \`send-message\` returns \`THREAD_BUSY\`, wait/read the active turn instead of sending another prompt.
|
|
@@ -158,7 +160,7 @@ open-computer-use call --calls '[
|
|
|
158
160
|
|
|
159
161
|
Always call \`get_app_state\` before using an \`element_index\`. Prefer semantic element actions such as \`click\`, \`set_value\`, and \`type_text\` over raw coordinates.
|
|
160
162
|
|
|
161
|
-
|
|
163
|
+
Happy Elves orchestration defaults to \`approve-all\` for trusted local sessions. Do not add \`--permission-mode approve-all\` just for this skill; only pass a permission mode when intentionally tightening or debugging one turn. If desktop probes fail, check the orchestrator response \`effectivePermissionMode\` before blaming macOS, Open Computer Use, or app permissions.
|
|
162
164
|
|
|
163
165
|
## Background-Only Mode
|
|
164
166
|
Use background-only mode when the user wants to keep using the computer while desktop dogfood runs. This mode is macOS-first; Windows and Linux support is best-effort and may require foreground desktop access.
|
|
@@ -252,6 +254,9 @@ const builtinSkills = [
|
|
|
252
254
|
"keep this orchestrator turn open",
|
|
253
255
|
"Strict handoff dogfood",
|
|
254
256
|
"git-commit-capability",
|
|
257
|
+
"effectivePermissionMode",
|
|
258
|
+
"trusted-local YOLO",
|
|
259
|
+
"prompt/model/thinking/permission",
|
|
255
260
|
"pnpm dogfood:orchestrator-real",
|
|
256
261
|
"do not ask the target agent to read an intermediate markdown file",
|
|
257
262
|
],
|
|
@@ -268,7 +273,7 @@ const builtinSkills = [
|
|
|
268
273
|
"OPEN_COMPUTER_USE_ALLOW_GLOBAL_POINTER_FALLBACKS",
|
|
269
274
|
"pnpm dogfood:computer-use-background",
|
|
270
275
|
"HAPPY_ELVES_CU_GATEWAY_CONFIRM=1",
|
|
271
|
-
"
|
|
276
|
+
"effectivePermissionMode",
|
|
272
277
|
"Live Feishu Gateway Dogfood",
|
|
273
278
|
"FOREGROUND_STOLEN",
|
|
274
279
|
"POINTER_MOVED",
|