@the-open-engine/zeroshot 6.39.0 → 6.39.2

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.
Files changed (88) hide show
  1. package/README.md +39 -1
  2. package/cli/index.js +90 -24
  3. package/cluster-templates/conductor-bootstrap.json +1 -1
  4. package/lib/agent-cli-provider/adapters/claude.js +1 -1
  5. package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
  6. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  7. package/lib/agent-cli-provider/adapters/codex.js +26 -1
  8. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  9. package/lib/agent-cli-provider/adapters/omp.d.ts.map +1 -1
  10. package/lib/agent-cli-provider/adapters/omp.js +1 -1
  11. package/lib/agent-cli-provider/adapters/omp.js.map +1 -1
  12. package/lib/agent-cli-provider/omp/sdk-environment-policy.d.ts +4 -0
  13. package/lib/agent-cli-provider/omp/sdk-environment-policy.d.ts.map +1 -0
  14. package/lib/agent-cli-provider/omp/sdk-environment-policy.js +37 -0
  15. package/lib/agent-cli-provider/omp/sdk-environment-policy.js.map +1 -0
  16. package/lib/agent-cli-provider/omp/sdk-process-private-runtime.d.ts.map +1 -1
  17. package/lib/agent-cli-provider/omp/sdk-process-private-runtime.js +4 -15
  18. package/lib/agent-cli-provider/omp/sdk-process-private-runtime.js.map +1 -1
  19. package/lib/agent-cli-provider/omp/sdk-process-result.d.ts +2 -0
  20. package/lib/agent-cli-provider/omp/sdk-process-result.d.ts.map +1 -1
  21. package/lib/agent-cli-provider/omp/sdk-process-result.js +2 -2
  22. package/lib/agent-cli-provider/omp/sdk-process-result.js.map +1 -1
  23. package/lib/agent-cli-provider/omp/sdk-process-runner.d.ts.map +1 -1
  24. package/lib/agent-cli-provider/omp/sdk-process-runner.js +6 -1
  25. package/lib/agent-cli-provider/omp/sdk-process-runner.js.map +1 -1
  26. package/lib/agent-cli-provider/provider-registry.d.ts +3 -1
  27. package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
  28. package/lib/agent-cli-provider/provider-registry.js +9 -2
  29. package/lib/agent-cli-provider/provider-registry.js.map +1 -1
  30. package/lib/agent-cli-provider/single-agent-runtime.d.ts +4 -1
  31. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  32. package/lib/agent-cli-provider/single-agent-runtime.js +18 -15
  33. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  34. package/lib/agent-cli-provider/types.d.ts +3 -0
  35. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  36. package/lib/agent-cli-provider/types.js.map +1 -1
  37. package/lib/cluster-worker/engine-adapter-common.js +18 -0
  38. package/lib/cluster-worker/engine-adapter.js +2 -1
  39. package/lib/detached-startup.d.ts +1 -0
  40. package/lib/detached-startup.js +2 -1
  41. package/lib/git-remote-utils.js +22 -5
  42. package/lib/start-cluster-environment.d.ts +1 -0
  43. package/lib/start-cluster-run-options.d.ts +1 -0
  44. package/lib/start-cluster-run-options.js +1 -0
  45. package/npm-shrinkwrap.json +6 -8
  46. package/package.json +4 -3
  47. package/scripts/omp/runtime-identities.js +31 -18
  48. package/scripts/omp/runtime.js +2 -0
  49. package/scripts/rust-distribution.js +13 -31
  50. package/src/agent/agent-lifecycle.js +17 -1
  51. package/src/agent/output-extraction-failures.js +73 -0
  52. package/src/agent/output-extraction-failures.ts +100 -0
  53. package/src/agent/output-extraction-types.ts +5 -0
  54. package/src/agent/provider-terminal-failure.js +2 -2
  55. package/src/agent/provider-terminal-failure.ts +4 -2
  56. package/src/agent-cli-provider/adapters/claude.ts +1 -1
  57. package/src/agent-cli-provider/adapters/codex.ts +36 -1
  58. package/src/agent-cli-provider/adapters/omp.ts +2 -1
  59. package/src/agent-cli-provider/omp/sdk-environment-policy.ts +38 -0
  60. package/src/agent-cli-provider/omp/sdk-process-private-runtime.ts +4 -16
  61. package/src/agent-cli-provider/omp/sdk-process-result.ts +4 -2
  62. package/src/agent-cli-provider/omp/sdk-process-runner.ts +7 -1
  63. package/src/agent-cli-provider/provider-registry.ts +10 -2
  64. package/src/agent-cli-provider/single-agent-runtime.ts +24 -15
  65. package/src/agent-cli-provider/types.ts +3 -0
  66. package/src/agents/git-pusher-template.js +78 -105
  67. package/src/attach/socket-paths.js +1 -1
  68. package/src/attach/socket-paths.ts +1 -1
  69. package/src/copy-containment.js +191 -0
  70. package/src/copy-containment.ts +259 -0
  71. package/src/copy-worker.js +29 -30
  72. package/src/copy-worker.ts +43 -30
  73. package/src/foreground-benchmark-run.js +24 -12
  74. package/src/isolation-manager.js +59 -20
  75. package/src/legacy-lib/detached-startup.ts +3 -1
  76. package/src/legacy-lib/git-remote-utils.ts +23 -5
  77. package/src/legacy-lib/start-cluster-environment.ts +1 -0
  78. package/src/legacy-lib/start-cluster-run-options.ts +2 -0
  79. package/src/omp-session-verifier.js +8 -5
  80. package/src/orchestrator.js +55 -3
  81. package/src/pr-body-template.js +71 -0
  82. package/src/preflight.js +31 -6
  83. package/src/providers/index.js +3 -3
  84. package/src/task-execution-context.js +22 -0
  85. package/src/watcher-prompt-channel.js +1 -1
  86. package/src/worktree-claude-config.js +25 -0
  87. package/src/worktree-claude-config.ts +28 -0
  88. package/task-lib/runner.js +57 -22
@@ -128,6 +128,7 @@ interface ProviderRegistryEntryBase {
128
128
  readonly authInstructions: string;
129
129
  readonly credentialPaths: readonly string[];
130
130
  readonly credentialEnvKeys: readonly string[];
131
+ readonly configurationEnvKeys?: readonly string[];
131
132
  readonly settingsFields: readonly string[];
132
133
  readonly settingsDefaults?: Readonly<Record<string, unknown>>;
133
134
  readonly settingsValidator?: (
@@ -185,6 +186,12 @@ const STANDARD_CAPABILITIES: Readonly<
185
186
 
186
187
  const CLAUDE_DOCKER_ENV_PASSTHROUGH = [
187
188
  'ANTHROPIC_API_KEY',
189
+ 'ANTHROPIC_AUTH_TOKEN',
190
+ 'ANTHROPIC_BASE_URL',
191
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL',
192
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL',
193
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL',
194
+ 'CLAUDE_CODE_SUBAGENT_MODEL',
188
195
  'AWS_BEARER_TOKEN_BEDROCK',
189
196
  'AWS_REGION',
190
197
  'CLAUDE_CODE_USE_BEDROCK',
@@ -198,7 +205,7 @@ const PI_EXPLICIT_DOCKER_CREDENTIALS = new Set([
198
205
  'GOOGLE_APPLICATION_CREDENTIALS',
199
206
  ]);
200
207
 
201
- const PI_DOCKER_CONFIGURATION_ENV = [
208
+ const PI_AGENT_CONFIGURATION_ENV = [
202
209
  'AWS_BEDROCK_FORCE_CACHE',
203
210
  'AWS_BEDROCK_FORCE_HTTP1',
204
211
  'AWS_BEDROCK_SKIP_AUTH',
@@ -504,7 +511,7 @@ export const providerRegistry = [
504
511
  configRoots: ['$HOME/.pi/agent'],
505
512
  envPassthrough: [
506
513
  ...piAdapter.credentialEnvKeys.filter((name) => !PI_EXPLICIT_DOCKER_CREDENTIALS.has(name)),
507
- ...PI_DOCKER_CONFIGURATION_ENV,
514
+ ...PI_AGENT_CONFIGURATION_ENV,
508
515
  ],
509
516
  },
510
517
  defaultLevels: {
@@ -527,6 +534,7 @@ export const providerRegistry = [
527
534
  'Manually edit providerSettings.omp in ZEROSHOT_SETTINGS_FILE or $HOME/.zeroshot/settings.json (file 0600, parent directory 0700). Use declared environment or broker variables, an explicit host-only OMP agent directory containing agent.db, or keyless mode; Zeroshot never logs in or stores credential values.',
528
535
  credentialPaths: ['~/.omp'],
529
536
  credentialEnvKeys: ompAdapter.credentialEnvKeys,
537
+ configurationEnvKeys: PI_AGENT_CONFIGURATION_ENV,
530
538
  settingsFields: [
531
539
  'transport',
532
540
  'minLevel',
@@ -4,6 +4,7 @@ import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
 
6
6
  import { getProviderAdapter } from './adapters';
7
+ import { providerCredentialEnv } from './contract-env';
7
8
  import { UnsupportedProviderCapabilityError } from './errors';
8
9
  import { normalizeGatewayBuildOptions, resolveGatewayConfiguration } from './gateway-tools';
9
10
  import { isRecord } from './json';
@@ -21,6 +22,7 @@ import {
21
22
  parseExactOmpModelSelector,
22
23
  resolveOmpSdkSettings,
23
24
  } from './omp/sdk-settings';
25
+ import { ompSdkEnvironmentPolicy } from './omp/sdk-environment-policy';
24
26
  import type { ConfiguredOmpSdkSettings } from './omp/sdk-settings';
25
27
  import {
26
28
  getDefaultProviderId,
@@ -124,6 +126,10 @@ const commandExistsFn = moduleFunction(providerDetectionModule, 'commandExists')
124
126
  const getHelpOutputFn = moduleFunction(providerDetectionModule, 'getHelpOutput');
125
127
  const getVersionOutputFn = moduleFunction(providerDetectionModule, 'getVersionOutput');
126
128
  const resolveOmpSdkRuntimeFn = moduleFunction(ompSdkRuntimeModule, 'resolveOmpSdkRuntime');
129
+ const resolveOmpSdkContainerRuntimeFn = moduleFunction(
130
+ ompSdkRuntimeModule,
131
+ 'resolveOmpSdkContainerRuntime'
132
+ );
127
133
 
128
134
  export const resolveOmpTransport = (runtimeSettings?: Record<string, unknown>): 'sdk' | 'rpc' => {
129
135
  const settings = runtimeSettings ?? loadRuntimeSettings();
@@ -325,14 +331,7 @@ function prepareOmpProviderCommand(
325
331
  }
326
332
 
327
333
  const usesContainerRuntime = executionContext === 'docker' || executionContext === 'benchmark';
328
- const runtime = usesContainerRuntime
329
- ? {
330
- bunExecutable: '/opt/zeroshot/node_modules/bun/bin/bun.exe',
331
- bunVersion: OMP_SDK_BUN_VERSION,
332
- ompVersion: OMP_SDK_BACKEND_VERSION,
333
- sidecarPath: '/opt/zeroshot/scripts/omp/sidecar.ts',
334
- }
335
- : ompSdkRuntimeAssets();
334
+ const runtime = usesContainerRuntime ? ompSdkContainerRuntimeAssets() : ompSdkRuntimeAssets();
336
335
  let privateRoot: string | undefined;
337
336
  try {
338
337
  privateRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'zeroshot-omp-sdk-request-'));
@@ -356,10 +355,7 @@ function prepareOmpProviderCommand(
356
355
  webSearch: { requested: false, effective: false },
357
356
  },
358
357
  invoke,
359
- environmentPolicy: {
360
- inherit: 'minimal',
361
- values: Object.freeze({}),
362
- },
358
+ environmentPolicy: ompSdkEnvironmentPolicy(),
363
359
  credentialNames,
364
360
  privateArtifacts: {
365
361
  root: privateRoot,
@@ -483,7 +479,14 @@ function requiredStringValue(value: unknown, field: string): string {
483
479
  }
484
480
 
485
481
  function ompSdkRuntimeAssets(): OmpSdkRuntimeAssets {
486
- const value = resolveOmpSdkRuntimeFn();
482
+ return validatedOmpSdkRuntime(resolveOmpSdkRuntimeFn());
483
+ }
484
+
485
+ function ompSdkContainerRuntimeAssets(): OmpSdkRuntimeAssets {
486
+ return validatedOmpSdkRuntime(resolveOmpSdkContainerRuntimeFn());
487
+ }
488
+
489
+ function validatedOmpSdkRuntime(value: unknown): OmpSdkRuntimeAssets {
487
490
  if (!isRecord(value))
488
491
  throw new Error('Pinned OMP SDK runtime resolver returned invalid evidence.');
489
492
  const runtime = {
@@ -640,7 +643,8 @@ function mergeAcpFailClosedCliFeatures(
640
643
  export function probeRuntimeProviderCli(
641
644
  provider: string,
642
645
  evidence?: RuntimeProbeEvidence,
643
- runtimeSettings?: Record<string, unknown>
646
+ runtimeSettings?: Record<string, unknown>,
647
+ context: { readonly executionContext?: OmpSdkExecutionContext } = {}
644
648
  ): RuntimeProviderProbe {
645
649
  const adapter = getProviderAdapter(provider);
646
650
  if (adapter.id === 'gateway') {
@@ -651,7 +655,11 @@ export function probeRuntimeProviderCli(
651
655
  if (adapter.id === 'omp' && resolveOmpTransport(settings) === 'sdk') {
652
656
  const capabilities = adapter.detectCliFeatures('', '');
653
657
  try {
654
- const runtime = ompSdkRuntimeAssets();
658
+ const executionContext = ompExecutionContext(context.executionContext);
659
+ const runtime =
660
+ executionContext === 'docker' || executionContext === 'benchmark'
661
+ ? ompSdkContainerRuntimeAssets()
662
+ : ompSdkRuntimeAssets();
655
663
  return {
656
664
  available: true,
657
665
  helpText: 'Pinned bundled OMP SDK sidecar',
@@ -881,6 +889,7 @@ function resolveRuntimeAuthEnv(
881
889
  provider: ProviderId,
882
890
  settings: Record<string, unknown>
883
891
  ): Readonly<Record<string, string>> {
892
+ if (provider === 'codex') return providerCredentialEnv(provider);
884
893
  if (provider !== 'claude') return {};
885
894
  const claudeAuthModule: unknown = require('../../lib/settings/claude-auth');
886
895
  const resolveClaudeAuth = moduleFunction(claudeAuthModule, 'resolveClaudeAuth');
@@ -131,6 +131,7 @@ export interface CodexCliFeatures extends BaseCliFeatures {
131
131
  readonly supportsIgnoreUserConfig: boolean;
132
132
  readonly supportsIgnoreRules: boolean;
133
133
  readonly supportsStrictConfig: boolean;
134
+ readonly supportsAddDir: boolean;
134
135
  }
135
136
 
136
137
  export interface GeminiCliFeatures extends BaseCliFeatures {
@@ -447,6 +448,8 @@ export interface BuildProviderCommandOptions {
447
448
  readonly outputFormat?: OutputFormat;
448
449
  readonly jsonSchema?: unknown;
449
450
  readonly cwd?: string;
451
+ /** Host paths Codex must be able to write in addition to cwd during normal unattended runs. */
452
+ readonly additionalWritableDirectories?: readonly string[];
450
453
  readonly agentName?: string;
451
454
  readonly autoApprove?: boolean;
452
455
  readonly resumeSessionId?: string;
@@ -277,6 +277,7 @@ return hasSufficientEvidence;`;
277
277
  const { readRepoSettings } = require('../../lib/repo-settings');
278
278
  const { normalizeGitRemoteName, quoteShellArgument } = require('../../lib/git-remote-utils');
279
279
  const { resolveRequiredQualityGates } = require('../quality-gates');
280
+ const { renderPullRequestBody, resolveIssueContext } = require('../pr-body-template');
280
281
 
281
282
  function getSafeBranchName(value) {
282
283
  if (typeof value !== 'string') {
@@ -314,35 +315,6 @@ function normalizeCloseIssueMode(value) {
314
315
  return null;
315
316
  }
316
317
 
317
- function normalizeIssueNumber(value) {
318
- const candidate = typeof value === 'number' ? String(value) : value;
319
- if (typeof candidate !== 'string') return 'unknown';
320
- const trimmed = candidate.trim();
321
- return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(trimmed) ? trimmed : 'unknown';
322
- }
323
-
324
- function normalizeIssueTitle(value) {
325
- if (typeof value !== 'string' || value.trim() === '') return 'Implementation';
326
- const normalized = [...value]
327
- .map((character) => {
328
- const codePoint = character.codePointAt(0);
329
- return codePoint < 0x20 || codePoint === 0x7f ? ' ' : character;
330
- })
331
- .join('')
332
- .trim();
333
- return normalized || 'Implementation';
334
- }
335
-
336
- function resolveIssueContext(options) {
337
- const issueNumber = normalizeIssueNumber(options.issueNumber);
338
- const issueTitle = normalizeIssueTitle(options.issueTitle);
339
- const issueReference =
340
- options.includeIssueReference === false || issueNumber === 'unknown'
341
- ? ''
342
- : `Closes #${issueNumber}`;
343
- return { issueNumber, issueTitle, issueReference };
344
- }
345
-
346
318
  /**
347
319
  * Resolve GitHub configuration from CLI options and repo settings.
348
320
  * Priority: CLI options > repo settings (.zeroshot/settings.json) > defaults
@@ -355,6 +327,7 @@ function resolveIssueContext(options) {
355
327
  * @param {string|number} [options.issueNumber] - Typed issue identifier for prompt commands
356
328
  * @param {string} [options.issueTitle] - Typed issue title for prompt commands
357
329
  * @param {boolean} [options.includeIssueReference] - Include the closing reference in PR text
330
+ * @param {string} [options.prBody] - Literal PR body template with supported issue tokens
358
331
  * @returns {Object} Resolved configuration
359
332
  */
360
333
  function resolveGitHubConfig(options = {}) {
@@ -385,16 +358,23 @@ function resolveGitHubConfig(options = {}) {
385
358
  options.autoMerge === true ||
386
359
  (options.autoMerge !== false && parseBool(repoGithub.autoMerge) === true);
387
360
 
361
+ const issueContext = resolveIssueContext(options);
362
+
388
363
  return {
389
364
  prBase,
390
365
  useMergeQueue,
391
366
  closeIssueMode,
392
367
  autoMerge,
393
368
  gitRemote,
394
- issueContext: resolveIssueContext(options),
369
+ issueContext,
370
+ prBody: renderPullRequestBody(options.prBody, options),
395
371
  };
396
372
  }
397
373
 
374
+ function resolvedPrBody(config, issueContext) {
375
+ return typeof config.prBody === 'string' ? config.prBody : issueContext.issueReference;
376
+ }
377
+
398
378
  /**
399
379
  * Generate platform-specific configuration based on resolved GitHub config.
400
380
  *
@@ -406,13 +386,15 @@ function getPlatformConfig(platform, config = {}) {
406
386
  const { prBase, useMergeQueue, closeIssueMode, autoMerge, gitRemote } = config;
407
387
  const issueContext = config.issueContext || resolveIssueContext({});
408
388
  const issueTitleArgument = quoteShellArgument(`feat: ${issueContext.issueTitle}`);
409
- const issueReferenceArgument = quoteShellArgument(issueContext.issueReference);
389
+ const prBodyArgument = quoteShellArgument(resolvedPrBody(config, issueContext));
410
390
 
411
391
  const PLATFORM_CONFIGS = {
412
392
  github: {
413
393
  prName: 'PR',
414
394
  prNameLower: 'pull request',
415
- createCmd: `gh pr create${prBase ? ` --base ${prBase}` : ''} --title ${issueTitleArgument} --body ${issueReferenceArgument}`,
395
+ createCmd:
396
+ `gh pr create${prBase ? ` --base ${prBase}` : ''} ` +
397
+ `--title ${issueTitleArgument} --body ${prBodyArgument}`,
416
398
  mergeCmd: useMergeQueue
417
399
  ? `PR_ID="$(timeout 30 gh pr view --json id --jq .id)"
418
400
  gh api graphql -f query='mutation($id:ID!){enqueuePullRequest(input:{pullRequestId:$id}){mergeQueueEntry{state}}}' -f id="$PR_ID"
@@ -434,7 +416,7 @@ for i in $(seq 1 90); do if timeout 30 gh pr view --json mergedAt --jq .mergedAt
434
416
  gitlab: {
435
417
  prName: 'MR',
436
418
  prNameLower: 'merge request',
437
- createCmd: `glab mr create --title ${issueTitleArgument} --description ${issueReferenceArgument}`,
419
+ createCmd: `glab mr create --title ${issueTitleArgument} --description ${prBodyArgument}`,
438
420
  mergeCmd: 'glab mr merge --auto-merge',
439
421
  mergeFallbackCmd: 'glab mr merge',
440
422
  prUrlExample: 'https://gitlab.com/owner/repo/-/merge_requests/123',
@@ -447,7 +429,7 @@ for i in $(seq 1 90); do if timeout 30 gh pr view --json mergedAt --jq .mergedAt
447
429
  'azure-devops': {
448
430
  prName: 'PR',
449
431
  prNameLower: 'pull request',
450
- createCmd: `az repos pr create --title ${issueTitleArgument} --description ${issueReferenceArgument}`,
432
+ createCmd: `az repos pr create --title ${issueTitleArgument} --description ${prBodyArgument}`,
451
433
  mergeCmd: 'az repos pr update --id <PR_ID> --auto-complete true',
452
434
  mergeFallbackCmd: 'az repos pr update --id <PR_ID> --status completed',
453
435
  prUrlExample: 'https://dev.azure.com/org/project/_git/repo/pullrequest/123',
@@ -475,18 +457,27 @@ for i in $(seq 1 90); do if timeout 30 gh pr view --json mergedAt --jq .mergedAt
475
457
  */
476
458
  const SUPPORTED_PLATFORMS = ['github', 'gitlab', 'azure-devops'];
477
459
 
478
- /**
479
- * Generate the review-mode prompt (--pr without --ship): create the PR/MR and STOP.
480
- * No merge step, no issue-closing - the PR is left open for human review.
481
- * @param {Object} config - Platform configuration from PLATFORM_CONFIGS
482
- * @returns {string} The complete review-mode prompt
483
- */
484
- function generateReviewModePrompt(config) {
460
+ function buildPushCommand(config, gitRemoteArgument) {
461
+ if (config.prName !== 'PR' || !config.createCmd.startsWith('gh pr create')) {
462
+ return `git push -u -- ${gitRemoteArgument} HEAD`;
463
+ }
464
+
465
+ const gitConfig = [
466
+ 'url.https://github.com/.insteadOf=git@github.com:',
467
+ 'url.https://github.com/.insteadOf=ssh://git@github.com/',
468
+ 'credential.helper=',
469
+ 'credential.helper=!gh auth git-credential',
470
+ ]
471
+ .map((value) => `-c ${quoteShellArgument(value)}`)
472
+ .join(' ');
473
+ return `git ${gitConfig} push -u -- ${gitRemoteArgument} HEAD`;
474
+ }
475
+
476
+ function generateDeliverySteps(config, createNote = '') {
485
477
  const {
486
478
  prName,
487
479
  prNameLower,
488
480
  createCmd,
489
- prUrlExample,
490
481
  outputFields,
491
482
  requiresPrIdExtraction,
492
483
  gitRemote,
@@ -496,28 +487,13 @@ function generateReviewModePrompt(config) {
496
487
  const commitMessageArgument = quoteShellArgument(
497
488
  `feat: implement #${issueContext.issueNumber} - ${issueContext.issueTitle}`
498
489
  );
490
+ const pushCommand = buildPushCommand(config, gitRemoteArgument);
491
+ const createCommandName = createCmd.split(' ').slice(0, 3).join(' ');
492
+ const prUrlInstruction = requiresPrIdExtraction
493
+ ? ''
494
+ : ` Save the actual ${prName} URL from the output.`;
499
495
 
500
- return `CRITICAL: ALL VALIDATORS APPROVED. YOU ARE A TRANSPORT-ONLY GIT PUSHER.
501
-
502
- Your job is to preserve validator ownership: stage, commit, push, and create the ${prName} for HUMAN REVIEW.
503
-
504
- Do NOT edit source files, tests, configs, generated artifacts, or lockfiles.
505
- Do NOT inspect CI logs to debug product code.
506
- Do NOT resolve merge conflicts or rebase conflicts.
507
- Do NOT run implementation/debugging workflows after validators hand off.
508
- Do NOT merge the ${prName} - it is left OPEN for human review.
509
- Do NOT close the linked issue - it stays open until a human merges the ${prName}.
510
-
511
- Allowed after validation:
512
- - git add/status/commit/push
513
- - ${createCmd.split(' ').slice(0, 3).join(' ')}
514
- - status-only commands such as ${prName === 'PR' ? 'gh pr view/gh pr checks' : 'the platform PR/MR status command'}
515
-
516
- If commit hooks, push, ${prName} creation, or conflict handling requires code changes, STOP and report the blocked state in JSON. The implementation and validator agents must fix code and rerun quality gates.
517
-
518
- ## MANDATORY STEPS - EXECUTE EACH ONE IN ORDER - DO NOT SKIP ANY STEP
519
-
520
- ### STEP 1: Stage ALL changes (MANDATORY)
496
+ return `### STEP 1: Stage ALL changes (MANDATORY)
521
497
  \`\`\`bash
522
498
  git add -A
523
499
  \`\`\`
@@ -537,7 +513,7 @@ Run this command. Do not skip it.
537
513
 
538
514
  ### STEP 4: Push to ${gitRemote} (MANDATORY)
539
515
  \`\`\`bash
540
- git push -u -- ${gitRemoteArgument} HEAD
516
+ ${pushCommand}
541
517
  \`\`\`
542
518
  Run this. If it fails, do not edit files, rebase, or resolve conflicts. Output blocked JSON with the failure summary.
543
519
 
@@ -547,9 +523,42 @@ Run this. If it fails, do not edit files, rebase, or resolve conflicts. Output b
547
523
  \`\`\`bash
548
524
  ${createCmd}
549
525
  \`\`\`
550
- 🚨 YOU MUST RUN \`${createCmd.split(' ').slice(0, 3).join(' ')}\`! Outputting a link is NOT creating a ${prName}! 🚨
526
+ 🚨 YOU MUST RUN \`${createCommandName}\`! Outputting a link is NOT creating a ${prName}! 🚨
551
527
  The push output shows a "Create a ${prNameLower}" link - IGNORE IT.
552
- You MUST run the \`${createCmd.split(' ').slice(0, 3).join(' ')}\` command above.${requiresPrIdExtraction ? '' : ` Save the actual ${prName} URL from the output.`}
528
+ You MUST run the \`${createCommandName}\` command above.${prUrlInstruction}${createNote}`;
529
+ }
530
+
531
+ /**
532
+ * Generate the review-mode prompt (--pr without --ship): create the PR/MR and STOP.
533
+ * No merge step, no issue-closing - the PR is left open for human review.
534
+ * @param {Object} config - Platform configuration from PLATFORM_CONFIGS
535
+ * @returns {string} The complete review-mode prompt
536
+ */
537
+ function generateReviewModePrompt(config) {
538
+ const { prName, createCmd, prUrlExample, outputFields } = config;
539
+ const deliverySteps = generateDeliverySteps(config);
540
+
541
+ return `CRITICAL: ALL VALIDATORS APPROVED. YOU ARE A TRANSPORT-ONLY GIT PUSHER.
542
+
543
+ Your job is to preserve validator ownership: stage, commit, push, and create the ${prName} for HUMAN REVIEW.
544
+
545
+ Do NOT edit source files, tests, configs, generated artifacts, or lockfiles.
546
+ Do NOT inspect CI logs to debug product code.
547
+ Do NOT resolve merge conflicts or rebase conflicts.
548
+ Do NOT run implementation/debugging workflows after validators hand off.
549
+ Do NOT merge the ${prName} - it is left OPEN for human review.
550
+ Do NOT close the linked issue - it stays open until a human merges the ${prName}.
551
+
552
+ Allowed after validation:
553
+ - git add/status/commit/push
554
+ - ${createCmd.split(' ').slice(0, 3).join(' ')}
555
+ - status-only commands such as ${prName === 'PR' ? 'gh pr view/gh pr checks' : 'the platform PR/MR status command'}
556
+
557
+ If commit hooks, push, ${prName} creation, or conflict handling requires code changes, STOP and report the blocked state in JSON. The implementation and validator agents must fix code and rerun quality gates.
558
+
559
+ ## MANDATORY STEPS - EXECUTE EACH ONE IN ORDER - DO NOT SKIP ANY STEP
560
+
561
+ ${deliverySteps}
553
562
 
554
563
  ⚠️ AFTER THE ${prName} IS CREATED, YOU ARE DONE. DO NOT MERGE. DO NOT CLOSE THE ISSUE. ⚠️
555
564
 
@@ -596,7 +605,6 @@ If blocked before creating a ${prName}, output:
596
605
  function generatePrompt(config) {
597
606
  const {
598
607
  prName,
599
- prNameLower,
600
608
  createCmd,
601
609
  mergeCmd,
602
610
  mergeFallbackCmd,
@@ -607,14 +615,9 @@ function generatePrompt(config) {
607
615
  usesMergeQueue,
608
616
  closeIssueMode,
609
617
  autoMerge,
610
- gitRemote,
611
618
  issueContext,
612
619
  } = config;
613
- const gitRemoteArgument = quoteShellArgument(gitRemote);
614
620
  const issueNumberArgument = quoteShellArgument(issueContext.issueNumber);
615
- const commitMessageArgument = quoteShellArgument(
616
- `feat: implement #${issueContext.issueNumber} - ${issueContext.issueTitle}`
617
- );
618
621
 
619
622
  if (!autoMerge) {
620
623
  return generateReviewModePrompt(config);
@@ -626,6 +629,7 @@ function generatePrompt(config) {
626
629
  Look for output like: "Created PR 123" or parse the URL for the PR number.
627
630
  Save the PR ID to a variable for step 6.`
628
631
  : '';
632
+ const deliverySteps = generateDeliverySteps(config, azurePrIdNote);
629
633
 
630
634
  // Azure uses different merge terminology
631
635
  const mergeDescription = requiresPrIdExtraction
@@ -684,39 +688,7 @@ If commit hooks, push, ${prName} creation, merge, CI, or conflict handling requi
684
688
 
685
689
  ## MANDATORY STEPS - EXECUTE EACH ONE IN ORDER - DO NOT SKIP ANY STEP
686
690
 
687
- ### STEP 1: Stage ALL changes (MANDATORY)
688
- \`\`\`bash
689
- git add -A
690
- \`\`\`
691
- Run this command. Do not skip it. If commit fails because hooks/checks fail, do not edit files. Output blocked JSON with the failure summary.
692
-
693
- ### STEP 2: Check what's staged
694
- \`\`\`bash
695
- git status
696
- \`\`\`
697
- Run this. If nothing to commit, output JSON with ${outputFields.urlField}: null and stop.
698
-
699
- ### STEP 3: Commit the changes (MANDATORY if there are changes)
700
- \`\`\`bash
701
- git commit -m ${commitMessageArgument}
702
- \`\`\`
703
- Run this command. Do not skip it.
704
-
705
- ### STEP 4: Push to ${gitRemote} (MANDATORY)
706
- \`\`\`bash
707
- git push -u -- ${gitRemoteArgument} HEAD
708
- \`\`\`
709
- Run this. If it fails, do not edit files, rebase, or resolve conflicts. Output blocked JSON with the failure summary.
710
-
711
- ⚠️ AFTER PUSH YOU ARE NOT DONE! CONTINUE TO STEP 5! ⚠️
712
-
713
- ### STEP 5: CREATE THE ${prName.toUpperCase()} (MANDATORY - YOU MUST RUN THIS COMMAND)
714
- \`\`\`bash
715
- ${createCmd}
716
- \`\`\`
717
- 🚨 YOU MUST RUN \`${createCmd.split(' ').slice(0, 3).join(' ')}\`! Outputting a link is NOT creating a ${prName}! 🚨
718
- The push output shows a "Create a ${prNameLower}" link - IGNORE IT.
719
- You MUST run the \`${createCmd.split(' ').slice(0, 3).join(' ')}\` command above.${requiresPrIdExtraction ? '' : ` Save the actual ${prName} URL from the output.`}${azurePrIdNote}
691
+ ${deliverySteps}
720
692
 
721
693
  ⚠️ AFTER ${prName} CREATION YOU ARE NOT DONE! CONTINUE TO STEP 6! ⚠️
722
694
 
@@ -803,6 +775,7 @@ If blocked before creating a ${prName}, output:
803
775
  * @param {string|number} [options.issueNumber] - Typed issue identifier for prompt commands
804
776
  * @param {string} [options.issueTitle] - Typed issue title for prompt commands
805
777
  * @param {boolean} [options.includeIssueReference] - Include the closing reference in PR text
778
+ * @param {string} [options.prBody] - Literal PR body template with supported issue tokens
806
779
  * @param {Array} [options.requiredQualityGates] - Required handoff quality gates
807
780
  * @param {boolean} [options.autoMerge] - Merge the PR (--ship). False stops after PR creation (--pr).
808
781
  * @returns {Object} Agent configuration object
@@ -3,7 +3,7 @@ const crypto = require("crypto");
3
3
  const fs = require("fs");
4
4
  const os = require("os");
5
5
  const path = require("path");
6
- const SOCKET_ROOT = process.platform === 'win32' ? null : '/tmp';
6
+ const SOCKET_ROOT = process.platform === 'win32' ? null : path.resolve(os.tmpdir());
7
7
  const SOCKET_DIR_MODE = 0o700;
8
8
  function shortHash(value) {
9
9
  return crypto.createHash('sha256').update(value).digest('hex').slice(0, 16);
@@ -3,7 +3,7 @@ import fs = require('fs');
3
3
  import os = require('os');
4
4
  import path = require('path');
5
5
 
6
- const SOCKET_ROOT = process.platform === 'win32' ? null : '/tmp';
6
+ const SOCKET_ROOT = process.platform === 'win32' ? null : path.resolve(os.tmpdir());
7
7
  const SOCKET_DIR_MODE = 0o700;
8
8
 
9
9
  function shortHash(value: string): string {