overlord-cli 0.2606010644.0 → 0.2606011152.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -74,6 +74,9 @@ ovld protocol search-tickets --query "auth refactor" --status next-up,execute
74
74
  ovld protocol update --session-key <session-key> --ticket-id <ticket_id> --summary "Working on it" --phase execute
75
75
  ovld protocol heartbeat --session-key <session-key> --ticket-id <ticket_id> --phase execute --percent 50 --note "Running tests"
76
76
  ovld runner start
77
+ ovld runner status
78
+ ovld runner clear <objective_id>
79
+ ovld runner clear-all
77
80
  ovld setup
78
81
  ovld setup codex
79
82
  ovld setup claude
@@ -148,12 +151,14 @@ Agents can find docs here: https://www.ovld.ai/docs/for-agents
148
151
  - `attachment-upload-file` - prepare, upload, and finalize a local objective attachment in one command
149
152
  - `get-device` / `update-device` - register the caller machine (per organization + user + fingerprint) and rename its label
150
153
  - `list-project-resources` / `add-project-resource` / `update-project-resource` - manage filesystem directories tied to a project device
151
- - `request-execution` / `claim-execution` / `complete-execution-launch` / `fail-execution-launch` - durable runner queue operations used by auto-advance and manual Run
154
+ - `request-execution` / `claim-execution` / `list-execution-requests` / `clear-execution-requests` / `complete-execution-launch` / `fail-execution-launch` - durable runner queue operations used by auto-advance and manual Run
152
155
  - `list-organizations` - list every organization the authenticated user (or agent token) belongs to; the runner uses this to poll all of your organizations, not just the one stored at login
153
156
 
154
157
  Devices are keyed by **(organization, user, fingerprint)** so the same physical workstation can appear once per org session.
155
158
 
156
- `ovld runner start` keeps a foreground runner alive for CLI-only or remote hosts. It claims compatible execution requests for the current device and launches the assigned agent/model with `ovld launch`; `ovld runner once` claims at most one request and exits. By default a single runner drains queued requests from **every organization you belong to**, so you do not need one runner per org. Pin it to a single org with `--organization-id <id>` or `OVERLORD_ORGANIZATION_ID`.
159
+ `ovld runner start` keeps a foreground runner alive for CLI-only or remote hosts. It claims compatible execution requests for the current device and launches the assigned agent/model with `ovld launch`; `ovld runner once` claims at most one request and exits. `ovld runner status` now prints the local device record plus the active queue visible to that runner. `ovld runner clear <objective_id>` clears one queued objective, and `ovld runner clear-all` clears every active queue row visible to the caller. By default a single runner drains queued requests from **every organization you belong to**, so you do not need one runner per org. Pin it to a single org with `--organization-id <id>` or `OVERLORD_ORGANIZATION_ID`.
160
+
161
+ If you are troubleshooting "why is `ovld runner` randomly executing", the runner is usually claiming a request that was already queued by manual Run or auto-advance. Inspect the queue with `ovld runner status`, then clear the specific objective with `ovld runner clear 8974e557-bec4-4984-b12c-be46bd63207c` or `ovld protocol clear-execution-requests --objective-id 8974e557-bec4-4984-b12c-be46bd63207c`.
157
162
 
158
163
  Use `create` for future work you want to track, `prompt` for work that should start immediately, and `record-work` for work that was already completed in chat and now needs a review ticket plus feed post. Use multiple tickets when prompts represent different features or goals; use `add-objectives` or `--objectives-json '[{"objective":"Step one"},{"objective":"Step two"}]'` when prompts are sequential steps toward the same feature or goal.
159
164
 
@@ -85,6 +85,18 @@ async function resolveProtocolMetadata(flags = {}, base = {}) {
85
85
  metadata.model = modelIdentifier;
86
86
  }
87
87
 
88
+ // Thread the originating execution request id (set by the runner via
89
+ // OVERLORD_EXECUTION_REQUEST_ID, or an explicit flag) into attach metadata so
90
+ // the server marks that exact request `launched` instead of falling back to
91
+ // matching by objective.
92
+ const executionRequestId =
93
+ (typeof flags['execution-request-id'] === 'string'
94
+ ? flags['execution-request-id'].trim()
95
+ : '') || process.env.OVERLORD_EXECUTION_REQUEST_ID?.trim();
96
+ if (executionRequestId) {
97
+ metadata.executionRequestId = executionRequestId;
98
+ }
99
+
88
100
  const snapshot = await resolveSnapshotContext(flags);
89
101
  if (snapshot) {
90
102
  metadata.snapshot = snapshot;
@@ -2067,6 +2079,58 @@ async function protocolListOrganizations(args) {
2067
2079
  console.log(JSON.stringify(data, null, 2));
2068
2080
  }
2069
2081
 
2082
+ async function protocolListExecutionRequests(args) {
2083
+ const flags = parseFlags(args);
2084
+ const { platformUrl, bearerToken, localSecret, organizationId } =
2085
+ await resolveProtocolAuthForFlags(flags);
2086
+ const timeoutMs = resolveTimeout(flags);
2087
+
2088
+ const data = await apiPost(
2089
+ platformUrl,
2090
+ bearerToken,
2091
+ localSecret,
2092
+ organizationId,
2093
+ '/api/protocol/list-execution-requests',
2094
+ {
2095
+ ...(typeof flags['device-fingerprint'] === 'string'
2096
+ ? { deviceFingerprint: flags['device-fingerprint'] }
2097
+ : {}),
2098
+ ...(typeof flags['project-id'] === 'string' ? { projectId: flags['project-id'] } : {})
2099
+ },
2100
+ timeoutMs
2101
+ );
2102
+ console.log(JSON.stringify(data, null, 2));
2103
+ }
2104
+
2105
+ async function protocolClearExecutionRequests(args) {
2106
+ const flags = parseFlags(args);
2107
+ const { platformUrl, bearerToken, localSecret, organizationId } =
2108
+ await resolveProtocolAuthForFlags(flags);
2109
+ const timeoutMs = resolveTimeout(flags);
2110
+
2111
+ const clearAll = flags['clear-all'] === true || flags['clear-all'] === 'true';
2112
+ const objectiveId = typeof flags['objective-id'] === 'string' ? flags['objective-id'].trim() : '';
2113
+ if (!clearAll && !objectiveId) {
2114
+ console.error('Error: --objective-id is required unless --clear-all is set');
2115
+ process.exit(1);
2116
+ }
2117
+
2118
+ const data = await apiPost(
2119
+ platformUrl,
2120
+ bearerToken,
2121
+ localSecret,
2122
+ organizationId,
2123
+ '/api/protocol/clear-execution-requests',
2124
+ {
2125
+ ...(objectiveId ? { objectiveId } : {}),
2126
+ ...(clearAll ? { clearAll: true } : {}),
2127
+ ...(typeof flags['project-id'] === 'string' ? { projectId: flags['project-id'] } : {})
2128
+ },
2129
+ timeoutMs
2130
+ );
2131
+ console.log(JSON.stringify(data, null, 2));
2132
+ }
2133
+
2070
2134
  async function protocolCompleteExecutionLaunch(args) {
2071
2135
  const flags = parseFlags(args);
2072
2136
  const { platformUrl, bearerToken, localSecret, organizationId } =
@@ -2663,6 +2727,8 @@ Subcommands:
2663
2727
  request-execution Queue an objective for local/remote runner execution
2664
2728
  claim-execution Claim one queued execution request for this device
2665
2729
  list-organizations List organizations the authenticated user belongs to
2730
+ list-execution-requests List active execution requests in the runner queue
2731
+ clear-execution-requests Clear active execution requests by objective or wholesale
2666
2732
  complete-execution-launch Mark a claimed execution request launched
2667
2733
  fail-execution-launch Mark a claimed execution request failed
2668
2734
  permission-request Notify Overlord that the agent is requesting tool permission
@@ -3164,6 +3230,25 @@ list-organizations:
3164
3230
  The runner uses this to poll all of your organizations, not just the one
3165
3231
  stored at login. Takes no required flags.
3166
3232
 
3233
+ list-execution-requests:
3234
+ Purpose:
3235
+ List active execution requests ('queued', 'claimed', 'launching') visible to
3236
+ the caller. When --device-fingerprint is provided, queued rows are filtered
3237
+ to work this execution target could claim and in-flight rows are limited to
3238
+ rows already claimed by this target.
3239
+ Optional:
3240
+ --device-fingerprint <fp> Filter queue visibility to one execution target
3241
+ --project-id <uuid> Restrict to one project
3242
+
3243
+ clear-execution-requests:
3244
+ Purpose:
3245
+ Clear active execution requests from the runner queue by marking them failed.
3246
+ Required:
3247
+ --objective-id <uuid> Clear one objective's active request
3248
+ or: --clear-all Clear every active request visible to the caller
3249
+ Optional:
3250
+ --project-id <uuid> Restrict clear-all to one project
3251
+
3167
3252
  complete-execution-launch:
3168
3253
  Required:
3169
3254
  --request-id <uuid> (or EXECUTION_REQUEST_ID env var)
@@ -3210,6 +3295,9 @@ Examples:
3210
3295
  ovld protocol request-execution --ticket-id 1:899 --agent codex --requested-from manual_run
3211
3296
  ovld protocol claim-execution --device-fingerprint $OVERLORD_DEVICE_FINGERPRINT
3212
3297
  ovld protocol list-organizations
3298
+ ovld protocol list-execution-requests --device-fingerprint $OVERLORD_DEVICE_FINGERPRINT
3299
+ ovld protocol clear-execution-requests --objective-id <objective-uuid>
3300
+ ovld protocol clear-execution-requests --clear-all
3213
3301
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done"
3214
3302
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --artifacts-file ./artifacts.json
3215
3303
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-json '{"summary":"Done","artifacts":[{"type":"note","label":"Delivery","content":"..."}]}'
@@ -3333,6 +3421,14 @@ EOF
3333
3421
  await protocolListOrganizations(args);
3334
3422
  return;
3335
3423
  }
3424
+ if (subcommand === 'list-execution-requests') {
3425
+ await protocolListExecutionRequests(args);
3426
+ return;
3427
+ }
3428
+ if (subcommand === 'clear-execution-requests') {
3429
+ await protocolClearExecutionRequests(args);
3430
+ return;
3431
+ }
3336
3432
  if (subcommand === 'complete-execution-launch') {
3337
3433
  await protocolCompleteExecutionLaunch(args);
3338
3434
  return;
@@ -22,6 +22,8 @@ function printRunnerHelp(primaryCommand = 'ovld') {
22
22
  ${primaryCommand} runner once [options]
23
23
  ${primaryCommand} runner start [options]
24
24
  ${primaryCommand} runner status [options]
25
+ ${primaryCommand} runner clear <objective_id> [options]
26
+ ${primaryCommand} runner clear-all [options]
25
27
 
26
28
  Options:
27
29
  --device-fingerprint <fp> Override runner device identity
@@ -114,6 +116,37 @@ function claimExecution(flags, deviceFingerprint, organizationId) {
114
116
  return protocolJson('claim-execution', args, { OVERLORD_DEVICE_FINGERPRINT: deviceFingerprint });
115
117
  }
116
118
 
119
+ function listExecutionRequests(flags, deviceFingerprint, organizationId) {
120
+ const args = ['--device-fingerprint', deviceFingerprint];
121
+ if (organizationId) {
122
+ args.push('--organization-id', String(organizationId));
123
+ }
124
+ if (typeof flags['project-id'] === 'string') {
125
+ args.push('--project-id', flags['project-id']);
126
+ }
127
+ return protocolJson('list-execution-requests', args, {
128
+ OVERLORD_DEVICE_FINGERPRINT: deviceFingerprint
129
+ });
130
+ }
131
+
132
+ function clearExecutionRequests(flags, deviceFingerprint, organizationId, options = {}) {
133
+ const args = [];
134
+ if (organizationId) {
135
+ args.push('--organization-id', String(organizationId));
136
+ }
137
+ if (typeof flags['project-id'] === 'string') {
138
+ args.push('--project-id', flags['project-id']);
139
+ }
140
+ if (options.clearAll) {
141
+ args.push('--clear-all');
142
+ } else if (options.objectiveId) {
143
+ args.push('--objective-id', options.objectiveId);
144
+ }
145
+ return protocolJson('clear-execution-requests', args, {
146
+ OVERLORD_DEVICE_FINGERPRINT: deviceFingerprint
147
+ });
148
+ }
149
+
117
150
  function listOrganizationIds(deviceFingerprint) {
118
151
  const data = protocolJson('list-organizations', [], {
119
152
  OVERLORD_DEVICE_FINGERPRINT: deviceFingerprint
@@ -138,13 +171,17 @@ function resolvePinnedOrganizationId(flags) {
138
171
  const ORGANIZATION_REFRESH_MS = 60_000;
139
172
 
140
173
  /**
141
- * Resolves which organizations a poll pass should claim from.
174
+ * Resolves which organizations a fan-out pass (list / clear-all) should act on.
142
175
  *
143
176
  * - When the user pins an org (`--organization-id` flag or
144
- * `OVERLORD_ORGANIZATION_ID`), only that org is polled.
145
- * - Otherwise the runner discovers every org the authenticated user belongs to
146
- * and polls each one. If discovery fails or returns nothing, it falls back to
147
- * the credential default (an empty scope id) so behavior never regresses.
177
+ * `OVERLORD_ORGANIZATION_ID`), only that org is used.
178
+ * - Otherwise the runner discovers every org the authenticated user belongs to.
179
+ * If discovery fails or returns nothing, it falls back to the credential
180
+ * default (an empty scope id) so behavior never regresses.
181
+ *
182
+ * NOTE: this is for the per-org fan-out commands. The claim poll uses
183
+ * {@link createClaimOrganizationScope}, which is org-agnostic — the server's
184
+ * `claim-execution` is authoritative and spans the user's target-sharing orgs.
148
185
  */
149
186
  export function createOrganizationScope(flags, deviceFingerprint) {
150
187
  const pinned = resolvePinnedOrganizationId(flags);
@@ -178,6 +215,17 @@ export function createOrganizationScope(flags, deviceFingerprint) {
178
215
  };
179
216
  }
180
217
 
218
+ /**
219
+ * Resolves the org scope for the claim poll (G3). A pinned org still scopes the
220
+ * poll; otherwise the runner does a single org-agnostic poll (empty scope) and
221
+ * lets the server claim across every org the user belongs to that shares the
222
+ * claiming target — no org discovery or per-org looping needed.
223
+ */
224
+ export function createClaimOrganizationScope(flags) {
225
+ const pinned = resolvePinnedOrganizationId(flags);
226
+ return { pinned: Boolean(pinned), resolve: () => [pinned || ''] };
227
+ }
228
+
181
229
  function completeLaunch(requestId, deviceFingerprint) {
182
230
  protocolJson(
183
231
  'complete-execution-launch',
@@ -243,11 +291,19 @@ function normalizeRunnerTerminalProfile(value) {
243
291
  };
244
292
  }
245
293
 
246
- function buildRunnerLaunchShellCommand(args, deviceFingerprint) {
247
- return [
248
- `export OVERLORD_DEVICE_FINGERPRINT=${shellQuote(deviceFingerprint)}`,
294
+ function buildRunnerLaunchShellCommand(args, deviceFingerprint, executionRequestId) {
295
+ const lines = [`export OVERLORD_DEVICE_FINGERPRINT=${shellQuote(deviceFingerprint)}`];
296
+ // Terminal-profile launches run this generated script in a fresh shell, so the
297
+ // request id must be exported here too (the direct-spawn path inherits it via
298
+ // the child env). Without it, attach in the new terminal falls back to
299
+ // objective matching.
300
+ if (executionRequestId) {
301
+ lines.push(`export OVERLORD_EXECUTION_REQUEST_ID=${shellQuote(executionRequestId)}`);
302
+ }
303
+ lines.push(
249
304
  `exec ${shellQuote(process.execPath)} ${[OVLD_ENTRY, ...args].map(shellQuote).join(' ')}`
250
- ].join('\n');
305
+ );
306
+ return lines.join('\n');
251
307
  }
252
308
 
253
309
  function writeRunnerLaunchScript(launchCommand) {
@@ -342,7 +398,7 @@ export function buildRunnerTerminalOpenCommand(
342
398
 
343
399
  function spawnLaunchProcess(args, claim, deviceFingerprint) {
344
400
  const spawnImpl = runnerTestHooks.spawn ?? spawn;
345
- const launchCommand = buildRunnerLaunchShellCommand(args, deviceFingerprint);
401
+ const launchCommand = buildRunnerLaunchShellCommand(args, deviceFingerprint, claim.request?.id);
346
402
  const terminalCommand = buildRunnerTerminalOpenCommand(
347
403
  claim.launch?.runnerTerminalProfile,
348
404
  launchCommand,
@@ -433,7 +489,7 @@ export async function launchClaimedRequest(claim, deviceFingerprint) {
433
489
  }
434
490
 
435
491
  export async function runOnce(flags, deviceFingerprint, organizationScope) {
436
- const scope = organizationScope ?? createOrganizationScope(flags, deviceFingerprint);
492
+ const scope = organizationScope ?? createClaimOrganizationScope(flags);
437
493
  const organizationIds = scope.resolve();
438
494
 
439
495
  let launchedAny = false;
@@ -466,6 +522,58 @@ function sleep(ms) {
466
522
  return new Promise(resolve => setTimeout(resolve, ms));
467
523
  }
468
524
 
525
+ async function collectQueue(flags, deviceFingerprint, organizationScope) {
526
+ const scope = organizationScope ?? createOrganizationScope(flags, deviceFingerprint);
527
+ const queue = [];
528
+ const errors = [];
529
+
530
+ for (const organizationId of scope.resolve()) {
531
+ try {
532
+ const data = listExecutionRequests(flags, deviceFingerprint, organizationId);
533
+ for (const request of data.requests ?? []) {
534
+ queue.push({
535
+ organizationId: organizationId || null,
536
+ ...request
537
+ });
538
+ }
539
+ } catch (error) {
540
+ errors.push({
541
+ organizationId: organizationId || null,
542
+ error: error instanceof Error ? error.message : String(error)
543
+ });
544
+ }
545
+ }
546
+
547
+ return { queue, errors, pinned: scope.pinned };
548
+ }
549
+
550
+ async function clearQueue(flags, deviceFingerprint, organizationScope, options) {
551
+ const scope = organizationScope ?? createOrganizationScope(flags, deviceFingerprint);
552
+ let clearedCount = 0;
553
+ const cleared = [];
554
+ const errors = [];
555
+
556
+ for (const organizationId of scope.resolve()) {
557
+ try {
558
+ const data = clearExecutionRequests(flags, deviceFingerprint, organizationId, options);
559
+ clearedCount += data.clearedCount ?? 0;
560
+ for (const request of data.requests ?? []) {
561
+ cleared.push({
562
+ organizationId: organizationId || null,
563
+ ...request
564
+ });
565
+ }
566
+ } catch (error) {
567
+ errors.push({
568
+ organizationId: organizationId || null,
569
+ error: error instanceof Error ? error.message : String(error)
570
+ });
571
+ }
572
+ }
573
+
574
+ return { clearedCount, cleared, errors, pinned: scope.pinned };
575
+ }
576
+
469
577
  export async function runRunnerCommand(subcommand, args, primaryCommand = 'ovld') {
470
578
  if (!subcommand || subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
471
579
  printRunnerHelp(primaryCommand);
@@ -476,6 +584,7 @@ export async function runRunnerCommand(subcommand, args, primaryCommand = 'ovld'
476
584
  const deviceFingerprint = readOrCreateDeviceFingerprint(flags);
477
585
 
478
586
  if (subcommand === 'status') {
587
+ const organizationScope = createOrganizationScope(flags, deviceFingerprint);
479
588
  const data = protocolJson(
480
589
  'get-device',
481
590
  [
@@ -488,7 +597,38 @@ export async function runRunnerCommand(subcommand, args, primaryCommand = 'ovld'
488
597
  ],
489
598
  { OVERLORD_DEVICE_FINGERPRINT: deviceFingerprint }
490
599
  );
491
- console.log(JSON.stringify({ ok: true, device: data.device }, null, 2));
600
+ const queue = await collectQueue(flags, deviceFingerprint, organizationScope);
601
+ console.log(
602
+ JSON.stringify(
603
+ {
604
+ ok: true,
605
+ device: data.device,
606
+ queue: queue.queue,
607
+ queueErrors: queue.errors
608
+ },
609
+ null,
610
+ 2
611
+ )
612
+ );
613
+ return;
614
+ }
615
+
616
+ if (subcommand === 'clear') {
617
+ const objectiveId = typeof args[0] === 'string' && !args[0].startsWith('--') ? args[0] : '';
618
+ if (!objectiveId) {
619
+ console.error(`Usage: ${primaryCommand} runner clear <objective_id> [options]`);
620
+ process.exit(1);
621
+ }
622
+ const remainingArgs = objectiveId ? args.slice(1) : args;
623
+ const clearFlags = parseFlags(remainingArgs);
624
+ const queue = await clearQueue(clearFlags, deviceFingerprint, null, { objectiveId });
625
+ console.log(JSON.stringify({ ok: true, ...queue }, null, 2));
626
+ return;
627
+ }
628
+
629
+ if (subcommand === 'clear-all') {
630
+ const queue = await clearQueue(flags, deviceFingerprint, null, { clearAll: true });
631
+ console.log(JSON.stringify({ ok: true, ...queue }, null, 2));
492
632
  return;
493
633
  }
494
634
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "0.2606010644.0",
3
+ "version": "0.2606011152.0",
4
4
  "description": "Overlord CLI — launch AI agents on tickets from anywhere",
5
5
  "homepage": "https://www.ovld.ai/docs/",
6
6
  "type": "module",
@@ -1054,6 +1054,40 @@ const tools = [
1054
1054
  }),
1055
1055
  subcommand: 'claim-execution'
1056
1056
  },
1057
+ {
1058
+ name: 'list_execution_requests',
1059
+ description: 'List active execution requests in the local runner queue.',
1060
+ inputSchema: {
1061
+ type: 'object',
1062
+ properties: {
1063
+ device_fingerprint: { type: 'string' },
1064
+ project_id: { type: 'string' }
1065
+ }
1066
+ },
1067
+ toCliFlags: args => ({
1068
+ 'device-fingerprint': args.device_fingerprint,
1069
+ 'project-id': args.project_id
1070
+ }),
1071
+ subcommand: 'list-execution-requests'
1072
+ },
1073
+ {
1074
+ name: 'clear_execution_requests',
1075
+ description: 'Clear active execution requests from the local runner queue.',
1076
+ inputSchema: {
1077
+ type: 'object',
1078
+ properties: {
1079
+ objective_id: { type: 'string' },
1080
+ clear_all: { type: 'boolean' },
1081
+ project_id: { type: 'string' }
1082
+ }
1083
+ },
1084
+ toCliFlags: args => ({
1085
+ 'objective-id': args.objective_id,
1086
+ ...(args.clear_all ? { 'clear-all': true } : {}),
1087
+ 'project-id': args.project_id
1088
+ }),
1089
+ subcommand: 'clear-execution-requests'
1090
+ },
1057
1091
  {
1058
1092
  name: 'complete_execution_launch',
1059
1093
  description: 'Mark a claimed execution request as launched.',
@@ -4,7 +4,7 @@
4
4
  - Hosted Overlord MCP (`/functions/v1/mcp`) uses camelCase JSON keys (`ticketId`, `sessionKey`, `deviceFingerprint`) matching `POST /api/protocol/*` bodies. The local shim uses snake_case keys mapped to CLI flags (`ticket_id`, `session_key`, `device_fingerprint`). Some local-only operations (`connect`, `load_ticket_context`, `revert`, `prompt`, runner queue tools) intentionally remain out of hosted MCP for now.
5
5
  - `discover_project` accepts an explicit project id (`projectId` hosted, `project_id` local shim) or a working directory (`workingDirectory` hosted, `working_directory` local shim). When resolving by directory and a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint` so resource-directory matching prefers the current device.
6
6
  - `permission-request` is invoked by the Claude Code permission hook installed by the bundle. Agents do not normally call it directly.
7
- - Execution request tools (`request_execution`, `claim_execution`, `complete_execution_launch`, `fail_execution_launch`) back the local runner queue. Agents normally work tickets with `attach`/`update`/`deliver`; runner processes use these queue tools.
7
+ - Execution request tools (`request_execution`, `claim_execution`, `list_execution_requests`, `clear_execution_requests`, `complete_execution_launch`, `fail_execution_launch`) back the local runner queue. Agents normally work tickets with `attach`/`update`/`deliver`; runner processes use these queue tools.
8
8
  - `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the same `file_changes` table. The dedicated CLI route is `POST /api/protocol/record-change-rationales`.
9
9
  - Objective attachment tools follow the `<verb>_<noun>` MCP naming: `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*`. `ticket_id` / `ticketId` is optional for attachment calls when the objective or attachment id lets the server derive ticket scope.
10
10
  - "Artifacts" in `deliver` are the structured records an agent submits at delivery time (next_steps, test_results, migration, decision, note, url) — not user-uploaded files. Files attached by users live on objectives via the attachment tools above.
@@ -4,7 +4,7 @@
4
4
  - Hosted Overlord MCP (`/functions/v1/mcp`) uses camelCase JSON keys (`ticketId`, `sessionKey`, `deviceFingerprint`) matching `POST /api/protocol/*` bodies. The local shim uses snake_case keys mapped to CLI flags (`ticket_id`, `session_key`, `device_fingerprint`). Some local-only operations (`connect`, `load_ticket_context`, `revert`, `prompt`, runner queue tools) intentionally remain out of hosted MCP for now.
5
5
  - `discover_project` accepts an explicit project id (`projectId` hosted, `project_id` local shim) or a working directory (`workingDirectory` hosted, `working_directory` local shim). When resolving by directory and a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint` so resource-directory matching prefers the current device.
6
6
  - `permission-request` is invoked by the Cursor permission hook installed by the bundle. Agents do not normally call it directly.
7
- - Execution request tools (`request_execution`, `claim_execution`, `complete_execution_launch`, `fail_execution_launch`) back the local runner queue. Agents normally work tickets with `attach`/`update`/`deliver`; runner processes use these queue tools.
7
+ - Execution request tools (`request_execution`, `claim_execution`, `list_execution_requests`, `clear_execution_requests`, `complete_execution_launch`, `fail_execution_launch`) back the local runner queue. Agents normally work tickets with `attach`/`update`/`deliver`; runner processes use these queue tools.
8
8
  - `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the same `file_changes` table. The dedicated CLI route is `POST /api/protocol/record-change-rationales`.
9
9
  - Objective attachment tools follow the `<verb>_<noun>` MCP naming: `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*`. `ticket_id` / `ticketId` is optional for attachment calls when the objective or attachment id lets the server derive ticket scope.
10
10
  - "Artifacts" in `deliver` are the structured records an agent submits at delivery time (next_steps, test_results, migration, decision, note, url) — not user-uploaded files. Files attached by users live on objectives via the attachment tools above.
@@ -1009,6 +1009,40 @@ const tools = [
1009
1009
  }),
1010
1010
  subcommand: 'claim-execution'
1011
1011
  },
1012
+ {
1013
+ name: 'list_execution_requests',
1014
+ description: 'List active execution requests in the local runner queue.',
1015
+ inputSchema: {
1016
+ type: 'object',
1017
+ properties: {
1018
+ device_fingerprint: { type: 'string' },
1019
+ project_id: { type: 'string' }
1020
+ }
1021
+ },
1022
+ toCliFlags: args => ({
1023
+ 'device-fingerprint': args.device_fingerprint,
1024
+ 'project-id': args.project_id
1025
+ }),
1026
+ subcommand: 'list-execution-requests'
1027
+ },
1028
+ {
1029
+ name: 'clear_execution_requests',
1030
+ description: 'Clear active execution requests from the local runner queue.',
1031
+ inputSchema: {
1032
+ type: 'object',
1033
+ properties: {
1034
+ objective_id: { type: 'string' },
1035
+ clear_all: { type: 'boolean' },
1036
+ project_id: { type: 'string' }
1037
+ }
1038
+ },
1039
+ toCliFlags: args => ({
1040
+ 'objective-id': args.objective_id,
1041
+ ...(args.clear_all ? { 'clear-all': true } : {}),
1042
+ 'project-id': args.project_id
1043
+ }),
1044
+ subcommand: 'clear-execution-requests'
1045
+ },
1012
1046
  {
1013
1047
  name: 'complete_execution_launch',
1014
1048
  description: 'Mark a claimed execution request as launched.',
@@ -4,7 +4,7 @@
4
4
  - Hosted Overlord MCP (`/functions/v1/mcp`) uses camelCase JSON keys (`ticketId`, `sessionKey`, `deviceFingerprint`) matching `POST /api/protocol/*` bodies. The local shim uses snake_case keys mapped to CLI flags (`ticket_id`, `session_key`, `device_fingerprint`). Some local-only operations (`connect`, `load_ticket_context`, `revert`, `prompt`, runner queue tools) intentionally remain out of hosted MCP for now.
5
5
  - `discover_project` accepts an explicit project id (`projectId` hosted, `project_id` local shim) or a working directory (`workingDirectory` hosted, `working_directory` local shim). When resolving by directory and a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint` so resource-directory matching prefers the current device.
6
6
  - `permission-request` is invoked by the Codex permission hook installed by the bundle. Agents do not normally call it directly.
7
- - Execution request tools (`request_execution`, `claim_execution`, `complete_execution_launch`, `fail_execution_launch`) back the local runner queue. Agents normally work tickets with `attach`/`update`/`deliver`; runner processes use these queue tools.
7
+ - Execution request tools (`request_execution`, `claim_execution`, `list_execution_requests`, `clear_execution_requests`, `complete_execution_launch`, `fail_execution_launch`) back the local runner queue. Agents normally work tickets with `attach`/`update`/`deliver`; runner processes use these queue tools.
8
8
  - `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the same `file_changes` table. The dedicated CLI route is `POST /api/protocol/record-change-rationales`.
9
9
  - Objective attachment tools follow the `<verb>_<noun>` MCP naming: `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*`. `ticket_id` / `ticketId` is optional for attachment calls when the objective or attachment id lets the server derive ticket scope.
10
10
  - "Artifacts" in `deliver` are the structured records an agent submits at delivery time (next_steps, test_results, migration, decision, note, url) — not user-uploaded files. Files attached by users live on objectives via the attachment tools above.