overlord-cli 0.2605211850.1 → 0.2605221356.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
@@ -65,6 +65,8 @@ ovld prompt "Draft a fix for the onboarding flow"
65
65
  ovld version
66
66
  ovld update
67
67
  ovld protocol discover-project
68
+ ovld protocol discover-project --project-id <project-id>
69
+ ovld protocol discover-project --working-directory /path/to/repo --device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"
68
70
  ovld protocol attach --ticket-id <ticket_id>
69
71
  ovld protocol search-tickets --query "auth refactor" --status next-up,execute
70
72
  ovld protocol update --session-key <session-key> --ticket-id <ticket_id> --summary "Working on it" --phase execute
@@ -1367,8 +1367,11 @@ async function protocolDeliver(args) {
1367
1367
  async function protocolAttachmentList(args) {
1368
1368
  const flags = parseFlags(args);
1369
1369
  const { sessionKey, ticketId } = resolveSessionFlags(flags);
1370
+ const objectiveId = flags['objective-id'] ? String(flags['objective-id']) : '';
1370
1371
  if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
1371
- if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
1372
+ if (!ticketId && !objectiveId) {
1373
+ throw new Error('--objective-id or --ticket-id is required');
1374
+ }
1372
1375
 
1373
1376
  const { platformUrl, bearerToken, localSecret, organizationId } =
1374
1377
  await resolveProtocolAuthForFlags(flags, ticketId);
@@ -1376,8 +1379,8 @@ async function protocolAttachmentList(args) {
1376
1379
 
1377
1380
  const body = {
1378
1381
  sessionKey,
1379
- ticketId,
1380
- ...(flags['objective-id'] ? { objectiveId: String(flags['objective-id']) } : {})
1382
+ ...(ticketId ? { ticketId } : {}),
1383
+ ...(objectiveId ? { objectiveId } : {})
1381
1384
  };
1382
1385
 
1383
1386
  const data = await apiPost(
@@ -1396,7 +1399,6 @@ async function protocolAttachmentPrepareUpload(args) {
1396
1399
  const flags = parseFlags(args);
1397
1400
  const { sessionKey, ticketId } = resolveSessionFlags(flags);
1398
1401
  if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
1399
- if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
1400
1402
  const objectiveId = requireFlag(flags, 'objective-id', undefined);
1401
1403
  const fileName = requireFlag(flags, 'file-name', undefined);
1402
1404
 
@@ -1406,7 +1408,7 @@ async function protocolAttachmentPrepareUpload(args) {
1406
1408
 
1407
1409
  const body = {
1408
1410
  sessionKey,
1409
- ticketId,
1411
+ ...(ticketId ? { ticketId } : {}),
1410
1412
  objectiveId,
1411
1413
  fileName,
1412
1414
  ...(flags.label ? { label: String(flags.label) } : {}),
@@ -1433,7 +1435,6 @@ async function protocolAttachmentFinalizeUpload(args) {
1433
1435
  const flags = parseFlags(args);
1434
1436
  const { sessionKey, ticketId } = resolveSessionFlags(flags);
1435
1437
  if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
1436
- if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
1437
1438
  const objectiveId = requireFlag(flags, 'objective-id', undefined);
1438
1439
  const storagePath = requireFlag(flags, 'storage-path', undefined);
1439
1440
  const label = requireFlag(flags, 'label', undefined);
@@ -1444,7 +1445,7 @@ async function protocolAttachmentFinalizeUpload(args) {
1444
1445
 
1445
1446
  const body = {
1446
1447
  sessionKey,
1447
- ticketId,
1448
+ ...(ticketId ? { ticketId } : {}),
1448
1449
  objectiveId,
1449
1450
  storagePath,
1450
1451
  label,
@@ -1471,7 +1472,6 @@ async function protocolAttachmentGetDownloadUrl(args) {
1471
1472
  const flags = parseFlags(args);
1472
1473
  const { sessionKey, ticketId } = resolveSessionFlags(flags);
1473
1474
  if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
1474
- if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
1475
1475
  if (!flags['attachment-id'] && !flags['storage-path']) {
1476
1476
  throw new Error('--attachment-id or --storage-path is required');
1477
1477
  }
@@ -1485,7 +1485,7 @@ async function protocolAttachmentGetDownloadUrl(args) {
1485
1485
 
1486
1486
  const body = {
1487
1487
  sessionKey,
1488
- ticketId,
1488
+ ...(ticketId ? { ticketId } : {}),
1489
1489
  ...(flags['objective-id'] ? { objectiveId: String(flags['objective-id']) } : {}),
1490
1490
  ...(flags['attachment-id'] ? { attachmentId: String(flags['attachment-id']) } : {}),
1491
1491
  ...(flags['storage-path'] ? { storagePath: String(flags['storage-path']) } : {}),
@@ -1508,7 +1508,6 @@ async function protocolAttachmentUploadFile(args) {
1508
1508
  const flags = parseFlags(args);
1509
1509
  const { sessionKey, ticketId } = resolveSessionFlags(flags);
1510
1510
  if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
1511
- if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
1512
1511
  const objectiveId = requireFlag(flags, 'objective-id', undefined);
1513
1512
  const filePath = requireFlag(flags, 'file', undefined);
1514
1513
 
@@ -1537,7 +1536,7 @@ async function protocolAttachmentUploadFile(args) {
1537
1536
  '/api/protocol/attachments/prepare-upload',
1538
1537
  {
1539
1538
  sessionKey,
1540
- ticketId,
1539
+ ...(ticketId ? { ticketId } : {}),
1541
1540
  objectiveId,
1542
1541
  fileName,
1543
1542
  label,
@@ -1564,7 +1563,7 @@ async function protocolAttachmentUploadFile(args) {
1564
1563
  '/api/protocol/attachments/finalize-upload',
1565
1564
  {
1566
1565
  sessionKey,
1567
- ticketId,
1566
+ ...(ticketId ? { ticketId } : {}),
1568
1567
  objectiveId,
1569
1568
  storagePath,
1570
1569
  label,
@@ -1588,7 +1587,8 @@ async function protocolDiscoverProject(args) {
1588
1587
  await resolveProtocolAuthForFlags(flags);
1589
1588
  const timeoutMs = resolveTimeout(flags);
1590
1589
 
1591
- const workingDirectory = String(flags['working-directory'] ?? process.cwd());
1590
+ const workingDirectory =
1591
+ flags['working-directory'] !== undefined ? String(flags['working-directory']) : process.cwd();
1592
1592
 
1593
1593
  const data = await apiPost(
1594
1594
  platformUrl,
@@ -1596,7 +1596,14 @@ async function protocolDiscoverProject(args) {
1596
1596
  localSecret,
1597
1597
  organizationId,
1598
1598
  '/api/protocol/discover-project',
1599
- { workingDirectory },
1599
+ {
1600
+ ...(flags['project-id'] ? { projectId: String(flags['project-id']) } : { workingDirectory }),
1601
+ ...(flags['device-fingerprint']
1602
+ ? { deviceFingerprint: String(flags['device-fingerprint']) }
1603
+ : {}),
1604
+ ...(flags['device-hostname'] ? { deviceHostname: String(flags['device-hostname']) } : {}),
1605
+ ...(flags['device-platform'] ? { devicePlatform: String(flags['device-platform']) } : {})
1606
+ },
1600
1607
  timeoutMs
1601
1608
  );
1602
1609
  console.log(JSON.stringify(data, null, 2));
@@ -2493,9 +2500,10 @@ Project discovery:
2493
2500
  You can also discover the project explicitly:
2494
2501
 
2495
2502
  ovld protocol discover-project
2503
+ ovld protocol discover-project --project-id <project-uuid>
2496
2504
  ovld protocol discover-project --working-directory /path/to/repo
2497
2505
 
2498
- Use --project-id to override automatic resolution on prompt or ticket creation.
2506
+ Use --project-id to override automatic resolution on discover, prompt, or ticket creation.
2499
2507
  Use --personal to create a private ticket without assigning any project.
2500
2508
 
2501
2509
  Subcommands:
@@ -2563,7 +2571,11 @@ discover-project:
2563
2571
  Resolve the Overlord project that corresponds to the current (or given) working directory.
2564
2572
  Uses the caller's configured "Local working directory" for matching.
2565
2573
  Optional:
2574
+ --project-id <uuid> Resolve this project directly; skips working-directory matching
2566
2575
  --working-directory <path> Directory to match (default: current working directory)
2576
+ --device-fingerprint <fp> Prefer resource directories for this registered device
2577
+ --device-hostname <name> Register/update device metadata when fingerprint is provided
2578
+ --device-platform <platform>
2567
2579
  Returns:
2568
2580
  Project JSON with id, name, organizationId. Prints PROJECT_ID=<id> on stderr.
2569
2581
  Notes:
@@ -2860,19 +2872,20 @@ record-work:
2860
2872
  attachment-list:
2861
2873
  Required:
2862
2874
  --session-key <key>
2863
- --ticket-id <ticket_id>
2875
+ one of: --objective-id <id> | --ticket-id <ticket_id>
2864
2876
  Optional:
2865
- --objective-id <id> Filter to a single objective
2877
+ --objective-id <id> Filter to a single objective (also acts as ticket scope)
2878
+ --ticket-id <ticket_id> List every attachment for a ticket
2866
2879
  Returns:
2867
2880
  JSON array of { id, label, content_type, file_size, objective_id, storage_path, created_at }
2868
2881
 
2869
2882
  attachment-prepare-upload:
2870
2883
  Required:
2871
2884
  --session-key <key>
2872
- --ticket-id <ticket_id>
2873
2885
  --objective-id <id>
2874
2886
  --file-name <name>
2875
2887
  Optional:
2888
+ --ticket-id <ticket_id> Derived from --objective-id when omitted
2876
2889
  --label <text>
2877
2890
  --content-type <mime>
2878
2891
  --file-size <bytes>
@@ -2881,11 +2894,11 @@ attachment-prepare-upload:
2881
2894
  attachment-finalize-upload:
2882
2895
  Required:
2883
2896
  --session-key <key>
2884
- --ticket-id <ticket_id>
2885
2897
  --objective-id <id>
2886
2898
  --storage-path <path>
2887
2899
  --label <text>
2888
2900
  Optional:
2901
+ --ticket-id <ticket_id> Derived from --objective-id when omitted
2889
2902
  --content-type <mime>
2890
2903
  --file-size <bytes>
2891
2904
  --metadata-json <json>
@@ -2893,19 +2906,19 @@ attachment-finalize-upload:
2893
2906
  attachment-download-url:
2894
2907
  Required:
2895
2908
  --session-key <key>
2896
- --ticket-id <ticket_id>
2897
2909
  one of: --attachment-id <id> | --storage-path <path>
2898
2910
  Optional:
2911
+ --ticket-id <ticket_id> Derived from --attachment-id / --objective-id when omitted
2899
2912
  --objective-id <id> Required when using --storage-path
2900
2913
  --expires-in <seconds>
2901
2914
 
2902
2915
  attachment-upload-file:
2903
2916
  Required:
2904
2917
  --session-key <key>
2905
- --ticket-id <ticket_id>
2906
2918
  --objective-id <id>
2907
2919
  --file <path>
2908
2920
  Optional:
2921
+ --ticket-id <ticket_id> Derived from --objective-id when omitted
2909
2922
  --file-name <name> Defaults to basename of --file
2910
2923
  --label <text> Defaults to file name
2911
2924
  --content-type <mime> Defaults to application/octet-stream
@@ -2993,6 +3006,7 @@ fail-execution-launch:
2993
3006
  Examples:
2994
3007
  ovld protocol auth-status
2995
3008
  ovld protocol discover-project
3009
+ ovld protocol discover-project --project-id <project-uuid>
2996
3010
  ovld protocol discover-project --working-directory /path/to/repo
2997
3011
  ovld protocol prompt --agent codex --objective "Implement feature X" # auto-resolves project from cwd
2998
3012
  ovld protocol attach --ticket-id 1:899
@@ -3015,10 +3029,10 @@ Examples:
3015
3029
  ovld protocol hook-event --hook-type UserPromptSubmit --ticket-id <ticket_id> --prompt "User follow-up" --turn-index 1
3016
3030
  ovld protocol read-context --session-key <key> --ticket-id <ticket_id> --query arch --limit 5
3017
3031
  ovld protocol write-context --session-key <key> --ticket-id <ticket_id> --key "arch" --value '"monorepo"' --tags repo,agent
3018
- ovld protocol attachment-list --session-key <key> --ticket-id <ticket_id>
3019
- ovld protocol attachment-prepare-upload --session-key <key> --ticket-id <ticket_id> --objective-id <objective-id> --file-name spec.pdf --content-type application/pdf
3020
- ovld protocol attachment-upload-file --session-key <key> --ticket-id <ticket_id> --objective-id <objective-id> --file ./spec.pdf
3021
- ovld protocol attachment-download-url --session-key <key> --ticket-id <ticket_id> --attachment-id <attachment-id>
3032
+ ovld protocol attachment-list --session-key <key> --objective-id <objective-id>
3033
+ ovld protocol attachment-prepare-upload --session-key <key> --objective-id <objective-id> --file-name spec.pdf --content-type application/pdf
3034
+ ovld protocol attachment-upload-file --session-key <key> --objective-id <objective-id> --file ./spec.pdf
3035
+ ovld protocol attachment-download-url --session-key <key> --attachment-id <attachment-id>
3022
3036
  ovld protocol request-execution --ticket-id 1:899 --agent codex --requested-from manual_run
3023
3037
  ovld protocol claim-execution --device-fingerprint $OVERLORD_DEVICE_FINGERPRINT
3024
3038
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "0.2605211850.1",
3
+ "version": "0.2605221356.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",
@@ -57,18 +57,40 @@ function execFileWithOptionalInput(file, args, options, input) {
57
57
  const tools = [
58
58
  {
59
59
  name: 'discover_project',
60
- description: 'Resolve the Overlord project that matches a working directory.',
60
+ description:
61
+ 'Resolve an Overlord project by explicit project_id or by matching a working directory.',
61
62
  inputSchema: {
62
63
  type: 'object',
63
64
  properties: {
65
+ project_id: {
66
+ type: 'string',
67
+ description: 'Project UUID to resolve directly. Skips working-directory matching.'
68
+ },
64
69
  working_directory: {
65
70
  type: 'string',
66
71
  description: 'Directory to match. Defaults to the current workspace.'
72
+ },
73
+ device_fingerprint: {
74
+ type: 'string',
75
+ description:
76
+ 'Optional stable device fingerprint. When provided, matching prefers resource directories for this registered device.'
77
+ },
78
+ device_hostname: {
79
+ type: 'string',
80
+ description: 'Optional hostname to register/update with device_fingerprint.'
81
+ },
82
+ device_platform: {
83
+ type: 'string',
84
+ description: 'Optional platform string, e.g. darwin, linux, or windows.'
67
85
  }
68
86
  }
69
87
  },
70
88
  toCliFlags: args => ({
71
- 'working-directory': args.working_directory
89
+ 'project-id': args.project_id,
90
+ 'working-directory': args.working_directory,
91
+ 'device-fingerprint': args.device_fingerprint,
92
+ 'device-hostname': args.device_hostname,
93
+ 'device-platform': args.device_platform
72
94
  }),
73
95
  subcommand: 'discover-project'
74
96
  },
@@ -437,7 +459,7 @@ const tools = [
437
459
  query: { type: 'string' },
438
460
  limit: { type: 'number' }
439
461
  },
440
- required: ['session_key', 'ticket_id']
462
+ required: ['session_key']
441
463
  },
442
464
  toCliFlags: args => ({
443
465
  'session-key': args.session_key,
@@ -584,7 +606,7 @@ const tools = [
584
606
  },
585
607
  objective_id: { type: 'string' }
586
608
  },
587
- required: ['session_key', 'ticket_id']
609
+ required: ['session_key']
588
610
  },
589
611
  toCliFlags: args => ({
590
612
  'session-key': args.session_key,
@@ -611,7 +633,7 @@ const tools = [
611
633
  file_size: { type: 'number' },
612
634
  metadata: { type: 'object' }
613
635
  },
614
- required: ['session_key', 'ticket_id', 'objective_id', 'file_name']
636
+ required: ['session_key', 'objective_id', 'file_name']
615
637
  },
616
638
  toCliFlags: args => ({
617
639
  'session-key': args.session_key,
@@ -644,7 +666,7 @@ const tools = [
644
666
  file_size: { type: 'number' },
645
667
  metadata: { type: 'object' }
646
668
  },
647
- required: ['session_key', 'ticket_id', 'objective_id', 'storage_path', 'label']
669
+ required: ['session_key', 'objective_id', 'storage_path', 'label']
648
670
  },
649
671
  toCliFlags: args => ({
650
672
  'session-key': args.session_key,
@@ -674,7 +696,7 @@ const tools = [
674
696
  storage_path: { type: 'string' },
675
697
  expires_in: { type: 'number' }
676
698
  },
677
- required: ['session_key', 'ticket_id']
699
+ required: ['session_key']
678
700
  },
679
701
  toCliFlags: args => ({
680
702
  'session-key': args.session_key,
@@ -705,7 +727,7 @@ const tools = [
705
727
  content_type: { type: 'string' },
706
728
  metadata: { type: 'object' }
707
729
  },
708
- required: ['session_key', 'ticket_id', 'objective_id', 'file']
730
+ required: ['session_key', 'objective_id', 'file']
709
731
  },
710
732
  toCliFlags: args => ({
711
733
  'session-key': args.session_key,
@@ -862,6 +884,113 @@ const tools = [
862
884
  : {})
863
885
  }),
864
886
  subcommand: 'update-project-resource'
887
+ },
888
+ {
889
+ name: 'request_execution',
890
+ description:
891
+ 'Queue an objective for local or remote runner execution. Manual Run and auto-advance use this durable queue.',
892
+ inputSchema: {
893
+ type: 'object',
894
+ properties: {
895
+ ticket_id: { type: 'string' },
896
+ objective_id: { type: 'string' },
897
+ requested_from: { type: 'string' },
898
+ idempotency_key: { type: 'string' },
899
+ agent: { type: 'string' },
900
+ model: { type: 'string' },
901
+ thinking: { type: 'string' },
902
+ launch_mode: { type: 'string', enum: ['run', 'ask'] },
903
+ working_directory: { type: 'string' },
904
+ ssh_command: { type: 'string' },
905
+ remote_working_directory: { type: 'string' },
906
+ server_multiplexer: { type: 'string', enum: ['none', 'tmux'] },
907
+ tmux_command: { type: 'string' },
908
+ target_kind: { type: 'string', enum: ['any', 'local', 'ssh'] },
909
+ target_device_id: { type: 'string' },
910
+ target_resource_id: { type: 'string' }
911
+ },
912
+ required: ['ticket_id']
913
+ },
914
+ toCliFlags: args => ({
915
+ 'ticket-id': args.ticket_id,
916
+ 'objective-id': args.objective_id,
917
+ 'requested-from': args.requested_from,
918
+ 'idempotency-key': args.idempotency_key,
919
+ agent: args.agent,
920
+ model: args.model,
921
+ thinking: args.thinking,
922
+ 'launch-mode': args.launch_mode,
923
+ 'working-directory': args.working_directory,
924
+ 'ssh-command': args.ssh_command,
925
+ 'remote-working-directory': args.remote_working_directory,
926
+ 'server-multiplexer': args.server_multiplexer,
927
+ 'tmux-command': args.tmux_command,
928
+ 'target-kind': args.target_kind,
929
+ 'target-device-id': args.target_device_id,
930
+ 'target-resource-id': args.target_resource_id
931
+ }),
932
+ subcommand: 'request-execution'
933
+ },
934
+ {
935
+ name: 'claim_execution',
936
+ description: 'Claim one queued execution request for this device.',
937
+ inputSchema: {
938
+ type: 'object',
939
+ properties: {
940
+ device_fingerprint: { type: 'string' },
941
+ device_hostname: { type: 'string' },
942
+ device_platform: { type: 'string' },
943
+ lease_seconds: { type: 'number' },
944
+ project_id: { type: 'string' }
945
+ },
946
+ required: ['device_fingerprint']
947
+ },
948
+ toCliFlags: args => ({
949
+ 'device-fingerprint': args.device_fingerprint,
950
+ 'device-hostname': args.device_hostname,
951
+ 'device-platform': args.device_platform,
952
+ 'lease-seconds': args.lease_seconds,
953
+ 'project-id': args.project_id
954
+ }),
955
+ subcommand: 'claim-execution'
956
+ },
957
+ {
958
+ name: 'complete_execution_launch',
959
+ description: 'Mark a claimed execution request as launched.',
960
+ inputSchema: {
961
+ type: 'object',
962
+ properties: {
963
+ request_id: { type: 'string' },
964
+ device_fingerprint: { type: 'string' },
965
+ launched_session_id: { type: 'string' }
966
+ },
967
+ required: ['request_id', 'device_fingerprint']
968
+ },
969
+ toCliFlags: args => ({
970
+ 'request-id': args.request_id,
971
+ 'device-fingerprint': args.device_fingerprint,
972
+ 'launched-session-id': args.launched_session_id
973
+ }),
974
+ subcommand: 'complete-execution-launch'
975
+ },
976
+ {
977
+ name: 'fail_execution_launch',
978
+ description: 'Mark a claimed execution request failed and record the launch error.',
979
+ inputSchema: {
980
+ type: 'object',
981
+ properties: {
982
+ request_id: { type: 'string' },
983
+ device_fingerprint: { type: 'string' },
984
+ error: { type: 'string' }
985
+ },
986
+ required: ['request_id', 'device_fingerprint', 'error']
987
+ },
988
+ toCliFlags: args => ({
989
+ 'request-id': args.request_id,
990
+ 'device-fingerprint': args.device_fingerprint,
991
+ error: args.error
992
+ }),
993
+ subcommand: 'fail-execution-launch'
865
994
  }
866
995
  ];
867
996
 
@@ -176,9 +176,11 @@ To inspect project resolution explicitly:
176
176
 
177
177
  ```bash
178
178
  ovld protocol discover-project
179
+ ovld protocol discover-project --project-id <project_uuid>
180
+ ovld protocol discover-project --working-directory /path/to/repo
179
181
  ```
180
182
 
181
- You can override with `--project-id` or `--working-directory` if needed.
183
+ Use `--project-id` when the project id is already known. Use `--working-directory` to override cwd path matching. If the runtime has an `OVERLORD_DEVICE_FINGERPRINT`, pass `--device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"` so resource-directory matching prefers the current device.
182
184
 
183
185
  ### Resolving the project ID when you don't have one
184
186
 
@@ -192,9 +194,9 @@ When you need a project ID for a protocol command and the ticket prompt did not
192
194
 
193
195
  **Over MCP (web agents and hosted tools, where the server cannot see the agent's cwd):**
194
196
 
195
- 1. `--project-id` / `projectId` if explicitly provided.
196
- 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId`.
197
- 3. As a last resort, try `workingDirectory` resolution.
197
+ 1. `projectId` (hosted MCP) or `project_id` (local shim) if explicitly provided or found in the ticket/context.
198
+ 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
199
+ 3. As a last resort, try `workingDirectory` / `working_directory` resolution. If a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint`.
198
200
 
199
201
  If `overlord.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
200
202
 
@@ -221,12 +223,12 @@ When in doubt, ask yourself: _can this be done entirely inside a terminal or bro
221
223
  ```bash
222
224
  ovld protocol read-context --session-key <sessionKey> --ticket-id $TICKET_ID
223
225
  ovld protocol write-context --session-key <sessionKey> --ticket-id $TICKET_ID --key "key" --value '"json-value"'
224
- ovld protocol attachment-list --session-key <sessionKey> --ticket-id $TICKET_ID
225
- ovld protocol attachment-upload-file --session-key <sessionKey> --ticket-id $TICKET_ID --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
226
- ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TICKET_ID --attachment-id <attachment-id>
226
+ ovld protocol attachment-list --session-key <sessionKey> --objective-id <objective-id>
227
+ ovld protocol attachment-upload-file --session-key <sessionKey> --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
228
+ ovld protocol attachment-download-url --session-key <sessionKey> --attachment-id <attachment-id>
227
229
  ```
228
230
 
229
- The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
231
+ The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach. `--ticket-id` is optional for attachment calls when `--objective-id` or `--attachment-id` lets the server derive the ticket.
230
232
 
231
233
  ## Large Artifacts
232
234
 
@@ -241,9 +243,10 @@ This keeps the ticket feed readable while preserving the full document in versio
241
243
  ## Defaults And Notes
242
244
 
243
245
  - API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `antigravity`/`cli`; the MCP tool defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
244
- - Hosted Overlord MCP (`/functions/v1/mcp`) uses the same canonical tool names as the local shim that shells into `ovld protocol` (`attach`, `update`, `deliver`, `get_device`, `list_project_resources`, …). Hosted calls use 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`).
246
+ - 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.
247
+ - `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.
245
248
  - `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`.
246
- - 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-*` and require `--objective-id` for upload/finalize.
249
+ - 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.
247
250
  - "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.
248
251
  - Antigravity CLI does not expose `--model`, `--thinking-level`, or `--temperature` flags. Model selection is managed by the Antigravity platform; do not attempt to pass these at launch.
249
252
  - The Overlord plugin is installed by `ovld setup antigravity`. Run this once to install the bundle into `~/.gemini/antigravity-cli/plugins/` and wire the MCP server path.
@@ -261,4 +264,4 @@ This keeps the ticket feed readable while preserving the full document in versio
261
264
  - Do not add or commit changes unless the user explicitly asks you to commit.
262
265
  - Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
263
266
 
264
- <!-- version: 0.1.0 -->
267
+ <!-- version: 0.1.1 -->
@@ -21,6 +21,28 @@ Use this mode when the prompt already contains a ticket ID or explicitly says th
21
21
 
22
22
  For full command syntax, flags, phase values, and event types see [reference/cli.md](reference/cli.md).
23
23
 
24
+ ## Tickets vs Objectives
25
+
26
+ **Tickets** represent whole features or goals. **Objectives** are the individual steps to implement that goal — one objective equals one agent prompt.
27
+
28
+ Example:
29
+ ```
30
+ Ticket: add CLI command for editing user profile
31
+ - Objective 1: draft plan for this command
32
+ - Objective 2: implement phase 1 of plan
33
+ - Objective 3: implement phase 2 of plan
34
+ - Objective 4: update documentation
35
+ ```
36
+
37
+ When to create a ticket vs an objective:
38
+ - **Create a new ticket** when the user describes a distinct feature, bug, or goal that stands on its own.
39
+ - **Add objectives to an existing ticket** when the work is a sequential step toward the same feature or goal already tracked in a ticket.
40
+
41
+ To add further objectives to an existing ticket (Mode 2):
42
+ ```
43
+ ovld protocol add-objectives --ticket-id <ticket_id> --objectives-json '[{"objective":"..."},{"objective":"..."}]'
44
+ ```
45
+
24
46
  ## Objective Submission vs Execution
25
47
 
26
48
  Discussing or otherwise opening a ticket from within a chat should cause the draft objective to be marked **submitted** — this signals the ticket is in active discussion with an agent, but not yet being executed. Only an explicit order to execute (e.g. "execute this", "do this", "start working on it") should cause you to **attach** to the ticket and trigger execution.
@@ -39,6 +61,7 @@ Use this mode when the conversation starts normally and the user asks Claude to
39
61
  - When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
40
62
  - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
41
63
  - Pass multiple items in `--objectives-json` when creating ordered steps for the same feature or goal.
64
+ - If the user wants to **add more objectives to an existing ticket** (not create a new ticket), use `ovld protocol add-objectives --ticket-id <ticket_id> --objectives-json '[{"objective":"..."}]'` instead.
42
65
  2. Default to `create` for new tickets. Only use `/overlord:prompt` or `ovld protocol prompt --agent claude-code --objectives-json '[{"objective":"..."}]'` when the user explicitly asks to create and execute immediately.
43
66
  `prompt` creates the ticket in `execute` status and attaches immediately.
44
67
  3. If the user already has a ticket ID and only wants to inspect it, use `/overlord:load` or run `ovld protocol load-context --ticket-id <ticket_id>`.
@@ -86,5 +109,5 @@ For the `record-change-rationales` command and full payload shape with optional
86
109
  - [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
87
110
  - [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
88
111
 
89
- <!-- version: 0.5.3 -->
112
+ <!-- version: 0.5.5 -->
90
113
 
@@ -118,9 +118,11 @@ To inspect project resolution explicitly:
118
118
 
119
119
  ```bash
120
120
  ovld protocol discover-project
121
+ ovld protocol discover-project --project-id <project_uuid>
122
+ ovld protocol discover-project --working-directory /path/to/repo
121
123
  ```
122
124
 
123
- You can override with `--project-id` or `--working-directory` if needed.
125
+ Use `--project-id` when the project id is already known. Use `--working-directory` to override cwd path matching. If the runtime has an `OVERLORD_DEVICE_FINGERPRINT`, pass `--device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"` so resource-directory matching prefers the current device.
124
126
 
125
127
  ### Resolving the project ID when you don't have one
126
128
 
@@ -134,9 +136,9 @@ When you need a project ID for a protocol command and the ticket prompt did not
134
136
 
135
137
  **Over MCP (web agents and hosted tools, where the server cannot see the agent's cwd):**
136
138
 
137
- 1. `--project-id` / `projectId` if explicitly provided.
138
- 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId`.
139
- 3. As a last resort, try `workingDirectory` resolution.
139
+ 1. `projectId` (hosted MCP) or `project_id` (local shim) if explicitly provided or found in the ticket/context.
140
+ 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
141
+ 3. As a last resort, try `workingDirectory` / `working_directory` resolution. If a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint`.
140
142
 
141
143
  If `overlord.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
142
144
 
@@ -3,12 +3,12 @@
3
3
  ```bash
4
4
  ovld protocol read-context --session-key <sessionKey> --ticket-id $TICKET_ID
5
5
  ovld protocol write-context --session-key <sessionKey> --ticket-id $TICKET_ID --key "key" --value '"json-value"'
6
- ovld protocol attachment-list --session-key <sessionKey> --ticket-id $TICKET_ID
7
- ovld protocol attachment-upload-file --session-key <sessionKey> --ticket-id $TICKET_ID --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
8
- ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TICKET_ID --attachment-id <attachment-id>
6
+ ovld protocol attachment-list --session-key <sessionKey> --objective-id <objective-id>
7
+ ovld protocol attachment-upload-file --session-key <sessionKey> --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
8
+ ovld protocol attachment-download-url --session-key <sessionKey> --attachment-id <attachment-id>
9
9
  ```
10
10
 
11
- The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
11
+ The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach. `--ticket-id` is optional for attachment calls when `--objective-id` or `--attachment-id` lets the server derive the ticket.
12
12
 
13
13
  ## Large Artifacts
14
14
 
@@ -1,10 +1,11 @@
1
1
  # MCP And API Defaults
2
2
 
3
3
  - API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `claude-code`/`cli`; the MCP tool defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
4
- - Hosted Overlord MCP (`/functions/v1/mcp`) uses the same canonical tool names as the local shim that shells into `ovld protocol` (`attach`, `update`, `deliver`, `get_device`, `list_project_resources`, …). Hosted calls use 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`).
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
+ - `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.
5
6
  - `permission-request` is invoked by the Claude Code permission hook installed by the bundle. Agents do not normally call it directly.
6
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
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`.
8
- - 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-*` and require `--objective-id` for upload/finalize.
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.
9
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.
10
11
 
@@ -21,6 +21,28 @@ Use this mode when the prompt already contains a ticket ID or explicitly says th
21
21
 
22
22
  For full command syntax, flags, phase values, and event types see [reference/cli.md](reference/cli.md).
23
23
 
24
+ ## Tickets vs Objectives
25
+
26
+ **Tickets** represent whole features or goals. **Objectives** are the individual steps to implement that goal — one objective equals one agent prompt.
27
+
28
+ Example:
29
+ ```
30
+ Ticket: add CLI command for editing user profile
31
+ - Objective 1: draft plan for this command
32
+ - Objective 2: implement phase 1 of plan
33
+ - Objective 3: implement phase 2 of plan
34
+ - Objective 4: update documentation
35
+ ```
36
+
37
+ When to create a ticket vs an objective:
38
+ - **Create a new ticket** when the user describes a distinct feature, bug, or goal that stands on its own.
39
+ - **Add objectives to an existing ticket** when the work is a sequential step toward the same feature or goal already tracked in a ticket.
40
+
41
+ To add further objectives to an existing ticket (Mode 2):
42
+ ```
43
+ ovld protocol add-objectives --ticket-id <ticket_id> --objectives-json '[{"objective":"..."},{"objective":"..."}]'
44
+ ```
45
+
24
46
  ## Objective Submission vs Execution
25
47
 
26
48
  Discussing or otherwise opening a ticket from within a chat should cause the draft objective to be marked **submitted** — this signals the ticket is in active discussion with an agent, but not yet being executed. Only an explicit order to execute (e.g. "execute this", "do this", "start working on it") should cause you to **attach** to the ticket and trigger execution.
@@ -39,6 +61,7 @@ Use this mode when the conversation starts normally and the user asks Cursor to
39
61
  - When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
40
62
  - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
41
63
  - Pass multiple items in `--objectives-json` when creating ordered steps for the same feature or goal.
64
+ - If the user wants to **add more objectives to an existing ticket** (not create a new ticket), use `ovld protocol add-objectives --ticket-id <ticket_id> --objectives-json '[{"objective":"..."}]'` instead.
42
65
  2. Default to `create` for new tickets. Only use `/prompt` or `ovld protocol prompt --agent cursor --objectives-json '[{"objective":"..."}]'` when the user explicitly asks to create and execute immediately.
43
66
  `prompt` creates the ticket in `execute` status and attaches immediately.
44
67
  3. If the user already has a ticket ID and only wants to inspect it, use `/load` or run `ovld protocol load-context --ticket-id <ticket_id>`.
@@ -86,5 +109,5 @@ For the `record-change-rationales` command and full payload shape with optional
86
109
  - [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
87
110
  - [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
88
111
 
89
- <!-- version: 0.5.3 -->
112
+ <!-- version: 0.5.5 -->
90
113
 
@@ -118,9 +118,11 @@ To inspect project resolution explicitly:
118
118
 
119
119
  ```bash
120
120
  ovld protocol discover-project
121
+ ovld protocol discover-project --project-id <project_uuid>
122
+ ovld protocol discover-project --working-directory /path/to/repo
121
123
  ```
122
124
 
123
- You can override with `--project-id` or `--working-directory` if needed.
125
+ Use `--project-id` when the project id is already known. Use `--working-directory` to override cwd path matching. If the runtime has an `OVERLORD_DEVICE_FINGERPRINT`, pass `--device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"` so resource-directory matching prefers the current device.
124
126
 
125
127
  ### Resolving the project ID when you don't have one
126
128
 
@@ -134,9 +136,9 @@ When you need a project ID for a protocol command and the ticket prompt did not
134
136
 
135
137
  **Over MCP (web agents and hosted tools, where the server cannot see the agent's cwd):**
136
138
 
137
- 1. `--project-id` / `projectId` if explicitly provided.
138
- 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId`.
139
- 3. As a last resort, try `workingDirectory` resolution.
139
+ 1. `projectId` (hosted MCP) or `project_id` (local shim) if explicitly provided or found in the ticket/context.
140
+ 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
141
+ 3. As a last resort, try `workingDirectory` / `working_directory` resolution. If a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint`.
140
142
 
141
143
  If `overlord.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
142
144
 
@@ -3,12 +3,12 @@
3
3
  ```bash
4
4
  ovld protocol read-context --session-key <sessionKey> --ticket-id $TICKET_ID
5
5
  ovld protocol write-context --session-key <sessionKey> --ticket-id $TICKET_ID --key "key" --value '"json-value"'
6
- ovld protocol attachment-list --session-key <sessionKey> --ticket-id $TICKET_ID
7
- ovld protocol attachment-upload-file --session-key <sessionKey> --ticket-id $TICKET_ID --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
8
- ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TICKET_ID --attachment-id <attachment-id>
6
+ ovld protocol attachment-list --session-key <sessionKey> --objective-id <objective-id>
7
+ ovld protocol attachment-upload-file --session-key <sessionKey> --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
8
+ ovld protocol attachment-download-url --session-key <sessionKey> --attachment-id <attachment-id>
9
9
  ```
10
10
 
11
- The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
11
+ The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach. `--ticket-id` is optional for attachment calls when `--objective-id` or `--attachment-id` lets the server derive the ticket.
12
12
 
13
13
  ## Large Artifacts
14
14
 
@@ -1,10 +1,11 @@
1
1
  # MCP And API Defaults
2
2
 
3
3
  - API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `cursor`/`cli`; the MCP tool defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
4
- - Hosted Overlord MCP (`/functions/v1/mcp`) uses the same canonical tool names as the local shim that shells into `ovld protocol` (`attach`, `update`, `deliver`, `get_device`, `list_project_resources`, …). Hosted calls use 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`).
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
+ - `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.
5
6
  - `permission-request` is invoked by the Cursor permission hook installed by the bundle. Agents do not normally call it directly.
6
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
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`.
8
- - 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-*` and require `--objective-id` for upload/finalize.
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.
9
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.
10
11
 
@@ -40,6 +40,7 @@ Tool names match the hosted Overlord MCP server (`supabase/functions/mcp/tools.t
40
40
  - Progress and review flow: `update`, `record_change_rationales`, `ask`, `deliver`
41
41
  - Shared context: `read_context`, `write_context`
42
42
  - Objective attachments: `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`
43
+ - Local runner queue: `request_execution`, `claim_execution`, `complete_execution_launch`, `fail_execution_launch`
43
44
 
44
45
  The MCP server shells into the installed `ovld` binary so the plugin stays aligned with the shipped CLI behavior instead of depending on this repository checkout.
45
46
 
@@ -57,18 +57,40 @@ function execFileWithOptionalInput(file, args, options, input) {
57
57
  const tools = [
58
58
  {
59
59
  name: 'discover_project',
60
- description: 'Resolve the Overlord project that matches a working directory.',
60
+ description:
61
+ 'Resolve an Overlord project by explicit project_id or by matching a working directory.',
61
62
  inputSchema: {
62
63
  type: 'object',
63
64
  properties: {
65
+ project_id: {
66
+ type: 'string',
67
+ description: 'Project UUID to resolve directly. Skips working-directory matching.'
68
+ },
64
69
  working_directory: {
65
70
  type: 'string',
66
71
  description: 'Directory to match. Defaults to the current workspace.'
72
+ },
73
+ device_fingerprint: {
74
+ type: 'string',
75
+ description:
76
+ 'Optional stable device fingerprint. When provided, matching prefers resource directories for this registered device.'
77
+ },
78
+ device_hostname: {
79
+ type: 'string',
80
+ description: 'Optional hostname to register/update with device_fingerprint.'
81
+ },
82
+ device_platform: {
83
+ type: 'string',
84
+ description: 'Optional platform string, e.g. darwin, linux, or windows.'
67
85
  }
68
86
  }
69
87
  },
70
88
  toCliFlags: args => ({
71
- 'working-directory': args.working_directory
89
+ 'project-id': args.project_id,
90
+ 'working-directory': args.working_directory,
91
+ 'device-fingerprint': args.device_fingerprint,
92
+ 'device-hostname': args.device_hostname,
93
+ 'device-platform': args.device_platform
72
94
  }),
73
95
  subcommand: 'discover-project'
74
96
  },
@@ -437,7 +459,7 @@ const tools = [
437
459
  query: { type: 'string' },
438
460
  limit: { type: 'number' }
439
461
  },
440
- required: ['session_key', 'ticket_id']
462
+ required: ['session_key']
441
463
  },
442
464
  toCliFlags: args => ({
443
465
  'session-key': args.session_key,
@@ -583,7 +605,7 @@ const tools = [
583
605
  },
584
606
  objective_id: { type: 'string' }
585
607
  },
586
- required: ['session_key', 'ticket_id']
608
+ required: ['session_key']
587
609
  },
588
610
  toCliFlags: args => ({
589
611
  'session-key': args.session_key,
@@ -610,7 +632,7 @@ const tools = [
610
632
  file_size: { type: 'number' },
611
633
  metadata: { type: 'object' }
612
634
  },
613
- required: ['session_key', 'ticket_id', 'objective_id', 'file_name']
635
+ required: ['session_key', 'objective_id', 'file_name']
614
636
  },
615
637
  toCliFlags: args => ({
616
638
  'session-key': args.session_key,
@@ -643,7 +665,7 @@ const tools = [
643
665
  file_size: { type: 'number' },
644
666
  metadata: { type: 'object' }
645
667
  },
646
- required: ['session_key', 'ticket_id', 'objective_id', 'storage_path', 'label']
668
+ required: ['session_key', 'objective_id', 'storage_path', 'label']
647
669
  },
648
670
  toCliFlags: args => ({
649
671
  'session-key': args.session_key,
@@ -673,7 +695,7 @@ const tools = [
673
695
  storage_path: { type: 'string' },
674
696
  expires_in: { type: 'number' }
675
697
  },
676
- required: ['session_key', 'ticket_id']
698
+ required: ['session_key']
677
699
  },
678
700
  toCliFlags: args => ({
679
701
  'session-key': args.session_key,
@@ -704,7 +726,7 @@ const tools = [
704
726
  content_type: { type: 'string' },
705
727
  metadata: { type: 'object' }
706
728
  },
707
- required: ['session_key', 'ticket_id', 'objective_id', 'file']
729
+ required: ['session_key', 'objective_id', 'file']
708
730
  },
709
731
  toCliFlags: args => ({
710
732
  'session-key': args.session_key,
@@ -861,6 +883,113 @@ const tools = [
861
883
  : {})
862
884
  }),
863
885
  subcommand: 'update-project-resource'
886
+ },
887
+ {
888
+ name: 'request_execution',
889
+ description:
890
+ 'Queue an objective for local or remote runner execution. Manual Run and auto-advance use this durable queue.',
891
+ inputSchema: {
892
+ type: 'object',
893
+ properties: {
894
+ ticket_id: { type: 'string' },
895
+ objective_id: { type: 'string' },
896
+ requested_from: { type: 'string' },
897
+ idempotency_key: { type: 'string' },
898
+ agent: { type: 'string' },
899
+ model: { type: 'string' },
900
+ thinking: { type: 'string' },
901
+ launch_mode: { type: 'string', enum: ['run', 'ask'] },
902
+ working_directory: { type: 'string' },
903
+ ssh_command: { type: 'string' },
904
+ remote_working_directory: { type: 'string' },
905
+ server_multiplexer: { type: 'string', enum: ['none', 'tmux'] },
906
+ tmux_command: { type: 'string' },
907
+ target_kind: { type: 'string', enum: ['any', 'local', 'ssh'] },
908
+ target_device_id: { type: 'string' },
909
+ target_resource_id: { type: 'string' }
910
+ },
911
+ required: ['ticket_id']
912
+ },
913
+ toCliFlags: args => ({
914
+ 'ticket-id': args.ticket_id,
915
+ 'objective-id': args.objective_id,
916
+ 'requested-from': args.requested_from,
917
+ 'idempotency-key': args.idempotency_key,
918
+ agent: args.agent,
919
+ model: args.model,
920
+ thinking: args.thinking,
921
+ 'launch-mode': args.launch_mode,
922
+ 'working-directory': args.working_directory,
923
+ 'ssh-command': args.ssh_command,
924
+ 'remote-working-directory': args.remote_working_directory,
925
+ 'server-multiplexer': args.server_multiplexer,
926
+ 'tmux-command': args.tmux_command,
927
+ 'target-kind': args.target_kind,
928
+ 'target-device-id': args.target_device_id,
929
+ 'target-resource-id': args.target_resource_id
930
+ }),
931
+ subcommand: 'request-execution'
932
+ },
933
+ {
934
+ name: 'claim_execution',
935
+ description: 'Claim one queued execution request for this device.',
936
+ inputSchema: {
937
+ type: 'object',
938
+ properties: {
939
+ device_fingerprint: { type: 'string' },
940
+ device_hostname: { type: 'string' },
941
+ device_platform: { type: 'string' },
942
+ lease_seconds: { type: 'number' },
943
+ project_id: { type: 'string' }
944
+ },
945
+ required: ['device_fingerprint']
946
+ },
947
+ toCliFlags: args => ({
948
+ 'device-fingerprint': args.device_fingerprint,
949
+ 'device-hostname': args.device_hostname,
950
+ 'device-platform': args.device_platform,
951
+ 'lease-seconds': args.lease_seconds,
952
+ 'project-id': args.project_id
953
+ }),
954
+ subcommand: 'claim-execution'
955
+ },
956
+ {
957
+ name: 'complete_execution_launch',
958
+ description: 'Mark a claimed execution request as launched.',
959
+ inputSchema: {
960
+ type: 'object',
961
+ properties: {
962
+ request_id: { type: 'string' },
963
+ device_fingerprint: { type: 'string' },
964
+ launched_session_id: { type: 'string' }
965
+ },
966
+ required: ['request_id', 'device_fingerprint']
967
+ },
968
+ toCliFlags: args => ({
969
+ 'request-id': args.request_id,
970
+ 'device-fingerprint': args.device_fingerprint,
971
+ 'launched-session-id': args.launched_session_id
972
+ }),
973
+ subcommand: 'complete-execution-launch'
974
+ },
975
+ {
976
+ name: 'fail_execution_launch',
977
+ description: 'Mark a claimed execution request failed and record the launch error.',
978
+ inputSchema: {
979
+ type: 'object',
980
+ properties: {
981
+ request_id: { type: 'string' },
982
+ device_fingerprint: { type: 'string' },
983
+ error: { type: 'string' }
984
+ },
985
+ required: ['request_id', 'device_fingerprint', 'error']
986
+ },
987
+ toCliFlags: args => ({
988
+ 'request-id': args.request_id,
989
+ 'device-fingerprint': args.device_fingerprint,
990
+ error: args.error
991
+ }),
992
+ subcommand: 'fail-execution-launch'
864
993
  }
865
994
  ];
866
995
  const searchTicketsTool = {
@@ -21,6 +21,28 @@ Use this mode when the prompt already contains a ticket ID or explicitly says th
21
21
 
22
22
  For full command syntax, flags, phase values, and event types see [reference/cli.md](reference/cli.md).
23
23
 
24
+ ## Tickets vs Objectives
25
+
26
+ **Tickets** represent whole features or goals. **Objectives** are the individual steps to implement that goal — one objective equals one agent prompt.
27
+
28
+ Example:
29
+ ```
30
+ Ticket: add CLI command for editing user profile
31
+ - Objective 1: draft plan for this command
32
+ - Objective 2: implement phase 1 of plan
33
+ - Objective 3: implement phase 2 of plan
34
+ - Objective 4: update documentation
35
+ ```
36
+
37
+ When to create a ticket vs an objective:
38
+ - **Create a new ticket** when the user describes a distinct feature, bug, or goal that stands on its own.
39
+ - **Add objectives to an existing ticket** when the work is a sequential step toward the same feature or goal already tracked in a ticket.
40
+
41
+ To add further objectives to an existing ticket (Mode 2):
42
+ ```
43
+ ovld protocol add-objectives --ticket-id <ticket_id> --objectives-json '[{"objective":"..."},{"objective":"..."}]'
44
+ ```
45
+
24
46
  ## Objective Submission vs Execution
25
47
 
26
48
  Discussing or otherwise opening a ticket from within a chat should cause the draft objective to be marked **submitted** — this signals the ticket is in active discussion with an agent, but not yet being executed. Only an explicit order to execute (e.g. "execute this", "do this", "start working on it") should cause you to **attach** to the ticket and trigger execution.
@@ -39,6 +61,7 @@ Use this mode when the conversation starts normally and the user asks Codex to c
39
61
  - When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
40
62
  - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
41
63
  - Pass multiple items in `--objectives-json` when creating ordered steps for the same feature or goal.
64
+ - If the user wants to **add more objectives to an existing ticket** (not create a new ticket), use `ovld protocol add-objectives --ticket-id <ticket_id> --objectives-json '[{"objective":"..."}]'` instead.
42
65
  2. Default to `create` for new tickets. Only use `ovld protocol prompt --agent codex --objectives-json '[{"objective":"..."}]'` when the user explicitly asks to create and execute immediately.
43
66
  `prompt` creates the ticket in `execute` status and attaches immediately.
44
67
  3. If the user already has a ticket ID and only wants to inspect it, run `ovld protocol load-context --ticket-id <ticket_id>`.
@@ -87,5 +110,5 @@ For the `record-change-rationales` command and full payload shape with optional
87
110
  - [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
88
111
  - [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
89
112
 
90
- <!-- version: 0.5.3 -->
113
+ <!-- version: 0.5.5 -->
91
114
 
@@ -118,9 +118,11 @@ To inspect project resolution explicitly:
118
118
 
119
119
  ```bash
120
120
  ovld protocol discover-project
121
+ ovld protocol discover-project --project-id <project_uuid>
122
+ ovld protocol discover-project --working-directory /path/to/repo
121
123
  ```
122
124
 
123
- You can override with `--project-id` or `--working-directory` if needed.
125
+ Use `--project-id` when the project id is already known. Use `--working-directory` to override cwd path matching. If the runtime has an `OVERLORD_DEVICE_FINGERPRINT`, pass `--device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"` so resource-directory matching prefers the current device.
124
126
 
125
127
  ### Resolving the project ID when you don't have one
126
128
 
@@ -134,9 +136,9 @@ When you need a project ID for a protocol command and the ticket prompt did not
134
136
 
135
137
  **Over MCP (web agents and hosted tools, where the server cannot see the agent's cwd):**
136
138
 
137
- 1. `--project-id` / `projectId` if explicitly provided.
138
- 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId`.
139
- 3. As a last resort, try `workingDirectory` resolution.
139
+ 1. `projectId` (hosted MCP) or `project_id` (local shim) if explicitly provided or found in the ticket/context.
140
+ 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
141
+ 3. As a last resort, try `workingDirectory` / `working_directory` resolution. If a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint`.
140
142
 
141
143
  If `overlord.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
142
144
 
@@ -3,12 +3,12 @@
3
3
  ```bash
4
4
  ovld protocol read-context --session-key <sessionKey> --ticket-id $TICKET_ID
5
5
  ovld protocol write-context --session-key <sessionKey> --ticket-id $TICKET_ID --key "key" --value '"json-value"'
6
- ovld protocol attachment-list --session-key <sessionKey> --ticket-id $TICKET_ID
7
- ovld protocol attachment-upload-file --session-key <sessionKey> --ticket-id $TICKET_ID --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
8
- ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TICKET_ID --attachment-id <attachment-id>
6
+ ovld protocol attachment-list --session-key <sessionKey> --objective-id <objective-id>
7
+ ovld protocol attachment-upload-file --session-key <sessionKey> --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
8
+ ovld protocol attachment-download-url --session-key <sessionKey> --attachment-id <attachment-id>
9
9
  ```
10
10
 
11
- The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
11
+ The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach. `--ticket-id` is optional for attachment calls when `--objective-id` or `--attachment-id` lets the server derive the ticket.
12
12
 
13
13
  ## Large Artifacts
14
14
 
@@ -1,10 +1,11 @@
1
1
  # MCP And API Defaults
2
2
 
3
3
  - API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `codex`/`cli`; the MCP tool defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
4
- - Hosted Overlord MCP (`/functions/v1/mcp`) uses the same canonical tool names as the local shim that shells into `ovld protocol` (`attach`, `update`, `deliver`, `get_device`, `list_project_resources`, …). Hosted calls use 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`).
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
+ - `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.
5
6
  - `permission-request` is invoked by the Codex permission hook installed by the bundle. Agents do not normally call it directly.
6
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
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`.
8
- - 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-*` and require `--objective-id` for upload/finalize.
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.
9
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.
10
11