@orchagent/cli 0.3.99 → 0.3.101

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.
@@ -9,18 +9,14 @@ const config_1 = require("../lib/config");
9
9
  const api_1 = require("../lib/api");
10
10
  const errors_1 = require("../lib/errors");
11
11
  const key_store_1 = require("../lib/key-store");
12
+ const resolve_agent_1 = require("../lib/resolve-agent");
12
13
  /**
13
14
  * Resolve an agent reference ("org/agent" or just "agent") to an agent ID.
14
- * Uses the authenticated list-agents endpoint and finds the latest version.
15
+ * Uses the shared resolveAgentContext() for parsing and org/workspace resolution,
16
+ * then finds the latest version via the authenticated list-agents endpoint.
15
17
  */
16
18
  async function resolveAgentId(config, ref) {
17
- const parts = ref.split('/');
18
- const agentName = parts.length >= 2 ? parts[1] : parts[0];
19
- const orgSlug = parts.length >= 2 ? parts[0] : undefined;
20
- // Resolve workspace context from org slug or config
21
- const configFile = await (0, config_1.loadConfig)();
22
- const resolvedOrg = orgSlug ?? configFile.workspace ?? config.defaultOrg;
23
- const workspaceId = resolvedOrg ? await (0, api_1.resolveWorkspaceIdForOrg)(config, resolvedOrg) : undefined;
19
+ const { org, agent: agentName, workspaceId } = await (0, resolve_agent_1.resolveAgentContext)(ref, config);
24
20
  const agents = await (0, api_1.listMyAgents)(config, workspaceId);
25
21
  const matching = agents.filter(a => a.name === agentName);
26
22
  if (matching.length === 0) {
@@ -28,7 +24,7 @@ async function resolveAgentId(config, ref) {
28
24
  }
29
25
  // Use the latest version
30
26
  const latest = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
31
- return { agent: latest, agentId: latest.id, orgSlug: latest.org_slug ?? resolvedOrg ?? '' };
27
+ return { agent: latest, agentId: latest.id, orgSlug: latest.org_slug ?? org, workspaceId };
32
28
  }
33
29
  function registerAgentKeysCommand(program) {
34
30
  const agentKeys = program
@@ -42,8 +38,8 @@ function registerAgentKeysCommand(program) {
42
38
  if (!config.apiKey) {
43
39
  throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
44
40
  }
45
- const { agent, agentId, orgSlug } = await resolveAgentId(config, ref);
46
- const result = await (0, api_1.listAgentKeys)(config, agentId);
41
+ const { agent, agentId, orgSlug, workspaceId } = await resolveAgentId(config, ref);
42
+ const result = await (0, api_1.listAgentKeys)(config, agentId, workspaceId);
47
43
  // Load locally-saved keys for this agent
48
44
  const localKeys = await (0, key_store_1.loadServiceKeys)(orgSlug, agent.name);
49
45
  const localPrefixes = new Set(localKeys.map(k => k.prefix));
@@ -76,8 +72,8 @@ function registerAgentKeysCommand(program) {
76
72
  if (!config.apiKey) {
77
73
  throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
78
74
  }
79
- const { agent, orgSlug } = await resolveAgentId(config, ref);
80
- const result = await (0, api_1.createAgentKey)(config, agent.id);
75
+ const { agent, orgSlug, workspaceId } = await resolveAgentId(config, ref);
76
+ const result = await (0, api_1.createAgentKey)(config, agent.id, workspaceId);
81
77
  process.stdout.write(`\nNew service key for ${agent.name}:\n\n`);
82
78
  process.stdout.write(` ${result.key}\n\n`);
83
79
  try {
@@ -96,8 +92,8 @@ function registerAgentKeysCommand(program) {
96
92
  if (!config.apiKey) {
97
93
  throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
98
94
  }
99
- const { agent, agentId } = await resolveAgentId(config, ref);
100
- await (0, api_1.deleteAgentKey)(config, agentId, keyId);
95
+ const { agent, agentId, workspaceId } = await resolveAgentId(config, ref);
96
+ await (0, api_1.deleteAgentKey)(config, agentId, keyId, workspaceId);
101
97
  process.stdout.write(`Deleted key ${keyId} from ${agent.name}.\n`);
102
98
  });
103
99
  }
@@ -246,8 +246,10 @@ function parseSecondRef(value, firstOrg, firstName) {
246
246
  const parsed = (0, agent_ref_1.parseAgentRef)(value);
247
247
  return { org: parsed.org ?? firstOrg, agent: parsed.agent, version: parsed.version };
248
248
  }
249
- // Otherwise treat as a version shorthand for the same agent
250
- return { org: firstOrg, agent: firstName, version: value };
249
+ // Otherwise treat as a version shorthand for the same agent.
250
+ // Strip leading '@' users naturally type `@v2` since the full form is `org/agent@v2`.
251
+ const version = value.startsWith('@') ? value.slice(1) : value;
252
+ return { org: firstOrg, agent: firstName, version };
251
253
  }
252
254
  // ── Command ────────────────────────────────────────────────────
253
255
  function registerDiffCommand(program) {
@@ -10,16 +10,21 @@ const DOCS_ROUTES = {
10
10
  '': '/',
11
11
  'cli': '/using-agents/cli-commands',
12
12
  'agents': '/building-agents/agent-types',
13
+ 'orchestration': '/building-agents/orchestration',
13
14
  'skills': '/building-agents/orchestration',
14
15
  'sdk': '/building-agents/sdk',
15
16
  'api': '/api-reference/overview',
16
17
  'quickstart': '/quickstart',
18
+ 'scheduling': '/using-agents/scheduling',
19
+ 'services': '/using-agents/services',
20
+ 'security': '/concepts/security',
21
+ 'billing': '/concepts/billing',
17
22
  };
18
23
  function registerDocsCommand(program) {
19
24
  program
20
25
  .command('docs')
21
26
  .description('Open documentation in browser')
22
- .argument('[topic]', 'Topic: cli, agents, skills, sdk, api, quickstart')
27
+ .argument('[topic]', 'Topic: cli, agents, orchestration, skills, sdk, api, quickstart, scheduling, services, security, billing')
23
28
  .action(async (topic) => {
24
29
  if (topic && !(topic in DOCS_ROUTES)) {
25
30
  const validTopics = Object.keys(DOCS_ROUTES).filter(k => k).join(', ');
@@ -43,9 +43,10 @@ function registerEstimateCommand(program) {
43
43
  process.exit(1);
44
44
  }
45
45
  const { agent, version } = parsed;
46
+ const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
46
47
  let data;
47
48
  try {
48
- data = await (0, api_1.getAgentCostEstimate)(config, org, agent, version);
49
+ data = await (0, api_1.getAgentCostEstimate)(config, org, agent, version, workspaceId);
49
50
  }
50
51
  catch (err) {
51
52
  if (err instanceof api_1.ApiError && err.status === 404) {
@@ -81,8 +81,9 @@ Examples:
81
81
  throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
82
82
  }
83
83
  const { agent, version } = parsed;
84
+ const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
84
85
  write('Resolving source agent...\n');
85
- const source = await (0, api_1.getAgentWithFallback)(config, org, agent, version);
86
+ const source = await (0, api_1.getAgentWithFallback)(config, org, agent, version, workspaceId);
86
87
  if (!source.id) {
87
88
  throw new errors_1.CliError(`Could not resolve source agent ID for '${org}/${agent}@${version}'.`);
88
89
  }
@@ -412,7 +412,8 @@ import asyncio
412
412
  import json
413
413
  import sys
414
414
 
415
- from orchagent import AgentClient
415
+ # pip install orchagent-sdk (package name)
416
+ from orchagent import AgentClient # module name
416
417
 
417
418
 
418
419
  async def run():
@@ -503,7 +504,8 @@ import asyncio
503
504
  import json
504
505
  import sys
505
506
 
506
- from orchagent import AgentClient
507
+ # pip install orchagent-sdk (package name)
508
+ from orchagent import AgentClient # module name
507
509
 
508
510
 
509
511
  async def run():
@@ -589,7 +591,8 @@ import asyncio
589
591
  import json
590
592
  import sys
591
593
 
592
- from orchagent import AgentClient
594
+ # pip install orchagent-sdk (package name)
595
+ from orchagent import AgentClient # module name
593
596
 
594
597
 
595
598
  async def run():
@@ -1207,7 +1210,8 @@ import asyncio
1207
1210
  import json
1208
1211
  import sys
1209
1212
 
1210
- from orchagent import AgentClient
1213
+ # pip install orchagent-sdk (package name)
1214
+ from orchagent import AgentClient # module name
1211
1215
 
1212
1216
 
1213
1217
  def main():
@@ -1490,7 +1494,10 @@ function resolveInitFlavor(typeOption) {
1490
1494
  if (normalized === 'prompt') {
1491
1495
  return { type: 'prompt', flavor: 'direct_llm' };
1492
1496
  }
1493
- if (normalized === 'agent' || normalized === 'agentic') {
1497
+ if (normalized === 'agent') {
1498
+ return { type: 'agent', flavor: 'managed_loop' };
1499
+ }
1500
+ if (normalized === 'agentic') {
1494
1501
  return { type: 'agent', flavor: 'code_runtime' };
1495
1502
  }
1496
1503
  if (normalized === 'tool' || normalized === 'code') {
@@ -1507,7 +1514,7 @@ function registerInitCommand(program) {
1507
1514
  .option('--orchestrator', 'Create an orchestrator agent with dependency scaffolding and SDK boilerplate')
1508
1515
  .option('--run-mode <mode>', 'Run mode for agents: on_demand or always_on', 'on_demand')
1509
1516
  .option('--language <lang>', 'Language: python or javascript (default: python)', 'python')
1510
- .option('--loop', 'Use platform-managed LLM loop instead of code runtime (requires --type agent)')
1517
+ .option('--loop', 'Use platform-managed LLM loop execution (explicit for --type agentic; default for --type agent)')
1511
1518
  .option('--template <name>', 'Start from a template (use --list-templates to see options)')
1512
1519
  .option('--list-templates', 'Show available templates with descriptions')
1513
1520
  .action(async (name, options) => {
@@ -1613,7 +1620,7 @@ function registerInitCommand(program) {
1613
1620
  const isJavaScript = ['javascript', 'js', 'typescript', 'ts'].includes(language);
1614
1621
  // Block unsupported JS flavors
1615
1622
  if (isJavaScript && initMode.flavor === 'managed_loop') {
1616
- throw new errors_1.CliError('JavaScript is not supported for --loop (managed loop). Use --type agent without --loop for a code-runtime agent.');
1623
+ throw new errors_1.CliError('JavaScript is not supported for managed-loop agents. Use --type agentic for a code-runtime agent scaffold.');
1617
1624
  }
1618
1625
  // JS orchestrators are now supported via the orchagent-sdk npm package
1619
1626
  // Block --language for types that don't create runtime files
@@ -2033,7 +2040,7 @@ function registerInitCommand(program) {
2033
2040
  }
2034
2041
  }
2035
2042
  if (initMode.flavor !== 'code_runtime' && initMode.flavor !== 'orchestrator' && initMode.flavor !== 'discord' && runMode === 'always_on') {
2036
- throw new errors_1.CliError("run_mode=always_on requires runtime.command in orchagent.json (e.g. \"runtime\": { \"command\": \"python main.py\" }). Use --type tool for code-runtime agents.");
2043
+ throw new errors_1.CliError("run_mode=always_on requires runtime.command in orchagent.json (e.g. \"runtime\": { \"command\": \"python main.py\" }). Use --type tool or --type agentic for code-runtime agents.");
2037
2044
  }
2038
2045
  // Create manifest and type-specific files
2039
2046
  const manifest = JSON.parse(MANIFEST_TEMPLATE);
@@ -10,24 +10,12 @@ const os_1 = __importDefault(require("os"));
10
10
  const config_1 = require("../lib/config");
11
11
  const api_1 = require("../lib/api");
12
12
  const errors_1 = require("../lib/errors");
13
+ const resolve_agent_1 = require("../lib/resolve-agent");
13
14
  const analytics_1 = require("../lib/analytics");
14
15
  const adapters_1 = require("../adapters");
15
16
  const skill_resolve_1 = require("../lib/skill-resolve");
16
17
  const installed_1 = require("../lib/installed");
17
18
  const agents_md_utils_1 = require("../lib/agents-md-utils");
18
- const DEFAULT_VERSION = 'latest';
19
- function parseAgentRef(value) {
20
- const [ref, versionPart] = value.split('@');
21
- const version = versionPart?.trim() || DEFAULT_VERSION;
22
- const segments = ref.split('/');
23
- if (segments.length === 1) {
24
- return { name: segments[0], version };
25
- }
26
- if (segments.length === 2) {
27
- return { org: segments[0], name: segments[1], version };
28
- }
29
- throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
30
- }
31
19
  async function downloadAgentWithFallback(config, org, name, version, workspaceId) {
32
20
  // Fetch public metadata first to check if paid
33
21
  let publicMeta;
@@ -135,19 +123,21 @@ function registerInstallCommand(program) {
135
123
  errors: [],
136
124
  };
137
125
  const resolved = await (0, config_1.getResolvedConfig)();
138
- const parsed = parseAgentRef(agentArg);
139
- const configFile = await (0, config_1.loadConfig)();
140
- const org = parsed.org ?? configFile.workspace ?? resolved.defaultOrg;
141
- if (!org) {
142
- if (jsonMode) {
143
- result.errors.push('Missing org. Use org/agent format or set default org.');
126
+ let agentCtx;
127
+ try {
128
+ agentCtx = await (0, resolve_agent_1.resolveAgentContext)(agentArg, resolved);
129
+ }
130
+ catch (err) {
131
+ if (jsonMode && err instanceof errors_1.CliError) {
132
+ result.errors.push(err.message);
144
133
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
145
134
  process.exit(errors_1.ExitCodes.INVALID_INPUT);
146
135
  }
147
- throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
136
+ throw err;
148
137
  }
149
- result.agent = `${org}/${parsed.name}`;
150
- result.version = parsed.version;
138
+ const { org, agent: agentName, version, workspaceId } = agentCtx;
139
+ result.agent = `${org}/${agentName}`;
140
+ result.version = version;
151
141
  // Determine target formats
152
142
  let targetFormats = [];
153
143
  if (options.format) {
@@ -186,13 +176,11 @@ function registerInstallCommand(program) {
186
176
  throw new errors_1.CliError(errMsg);
187
177
  }
188
178
  result.scope = scope;
189
- // Resolve workspace context for the target org
190
- const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(resolved, org);
191
179
  // Download agent
192
- log(`Fetching ${org}/${parsed.name}@${parsed.version}...\n`);
180
+ log(`Fetching ${org}/${agentName}@${version}...\n`);
193
181
  let agent;
194
182
  try {
195
- agent = await downloadAgentWithFallback(resolved, org, parsed.name, parsed.version, workspaceId);
183
+ agent = await downloadAgentWithFallback(resolved, org, agentName, version, workspaceId);
196
184
  }
197
185
  catch (err) {
198
186
  if (jsonMode) {
@@ -273,15 +261,15 @@ function registerInstallCommand(program) {
273
261
  catch {
274
262
  // File doesn't exist, will create new
275
263
  }
276
- const agentRef = `${org}/${parsed.name}`;
264
+ const agentRef = `${org}/${agentName}`;
277
265
  file.content = (0, agents_md_utils_1.mergeAgentsMdContent)(existingContent, file.content, agentRef);
278
266
  }
279
267
  await promises_1.default.writeFile(fullPath, file.content);
280
268
  filesWritten++;
281
269
  // Track installation
282
270
  const installedAgent = {
283
- agent: `${org}/${parsed.name}`,
284
- version: parsed.version,
271
+ agent: `${org}/${agentName}`,
272
+ version: version,
285
273
  format: formatId,
286
274
  scope: effectiveScope,
287
275
  path: fullPath,
@@ -297,7 +285,7 @@ function registerInstallCommand(program) {
297
285
  if (!options.dryRun) {
298
286
  if (filesWritten > 0) {
299
287
  await (0, analytics_1.track)('cli_agent_install', {
300
- agent: `${org}/${parsed.name}`,
288
+ agent: `${org}/${agentName}`,
301
289
  formats: targetFormats,
302
290
  scope,
303
291
  });
@@ -182,7 +182,7 @@ async function listRuns(config, workspaceId, agentName, options) {
182
182
  if (result.total > result.runs.length) {
183
183
  process.stdout.write(chalk_1.default.gray(`\nShowing ${result.runs.length} of ${result.total} runs. Use --limit to see more.\n`));
184
184
  }
185
- process.stdout.write(chalk_1.default.gray('\nView detailed logs for a run: orch logs <run-id>\n'));
185
+ process.stdout.write(chalk_1.default.gray('\nView detailed logs for a run: orch logs <run-id> · Replay a run: orch replay <run-id>\n'));
186
186
  }
187
187
  // ============================================
188
188
  // SHOW RUN LOGS
@@ -229,5 +229,7 @@ async function showRunLogs(config, workspaceId, runId, json) {
229
229
  process.stdout.write(chalk_1.default.gray('\nNo sandbox output available for this run. ' +
230
230
  'Execution logs are captured for agents with a code runtime (tool/agent types with runtime.command).\n'));
231
231
  }
232
+ // Footer hints
233
+ process.stdout.write(chalk_1.default.gray(`\nReplay: orch replay ${runId.slice(0, 8)} · Trace: orch trace ${runId.slice(0, 8)}\n`));
232
234
  process.stdout.write('\n');
233
235
  }
@@ -510,6 +510,8 @@ async function batchPublish(rootDir, options) {
510
510
  forwardArgs.push('--no-local-download');
511
511
  if (options.requiredSecrets === false)
512
512
  forwardArgs.push('--no-required-secrets');
513
+ if (options.verbose)
514
+ forwardArgs.push('--verbose');
513
515
  const results = [];
514
516
  for (let i = 0; i < sorted.length; i++) {
515
517
  const agent = sorted[i];
@@ -586,6 +588,7 @@ function registerPublishCommand(program) {
586
588
  .option('--no-local-download', 'Prevent users from downloading and running locally (default: allowed)')
587
589
  .option('--no-required-secrets', '(deprecated) No longer needed — required_secrets defaults to []')
588
590
  .option('--all', 'Publish all agents in subdirectories (dependency order)')
591
+ .option('--verbose', 'Show detailed bundle contents (file list)')
589
592
  .action(async (options) => {
590
593
  const cwd = process.cwd();
591
594
  // --all: batch publish all agents in subdirectories
@@ -696,6 +699,11 @@ function registerPublishCommand(program) {
696
699
  process.stdout.write(`\n${chalk_1.default.green('✔')} Published ${org.slug}/${skillData.frontmatter.name}@${skillVersion} successfully!\n\n`);
697
700
  if (hasMultipleFiles) {
698
701
  process.stdout.write(`Files: ${skillFiles.length} files included\n`);
702
+ if (options.verbose) {
703
+ for (const f of skillFiles) {
704
+ process.stdout.write(` ${f.path}\n`);
705
+ }
706
+ }
699
707
  }
700
708
  process.stdout.write(`Visibility: private\n`);
701
709
  process.stdout.write(`\nView analytics and usage: https://orchagent.io/dashboard\n`);
@@ -1061,6 +1069,12 @@ function registerPublishCommand(program) {
1061
1069
  process.stderr.write(` Files: ${bundlePreview.fileCount} files\n`);
1062
1070
  process.stderr.write(` Size: ${(bundlePreview.totalSizeBytes / 1024).toFixed(1)} KB\n`);
1063
1071
  process.stderr.write(` Entrypoint: ${bundlePreview.entrypoint}\n`);
1072
+ if (options.verbose && bundlePreview.files?.length) {
1073
+ process.stderr.write(` Bundled files:\n`);
1074
+ for (const f of bundlePreview.files) {
1075
+ process.stderr.write(` ${f}\n`);
1076
+ }
1077
+ }
1064
1078
  }
1065
1079
  process.stderr.write('\nAgent Preview:\n');
1066
1080
  process.stderr.write(` Name: ${manifest.name}\n`);
@@ -1324,6 +1338,12 @@ function registerPublishCommand(program) {
1324
1338
  skipEntrypointCheck: false,
1325
1339
  });
1326
1340
  process.stdout.write(` Created bundle: ${bundleResult.fileCount} files, ${(bundleResult.sizeBytes / 1024).toFixed(1)}KB\n`);
1341
+ if (options.verbose && bundleResult.files?.length) {
1342
+ process.stdout.write(` Bundled files:\n`);
1343
+ for (const f of bundleResult.files) {
1344
+ process.stdout.write(` ${f}\n`);
1345
+ }
1346
+ }
1327
1347
  // Validate bundle size
1328
1348
  const validation = await (0, bundle_1.validateBundle)(bundlePath);
1329
1349
  if (!validation.valid) {
@@ -1404,14 +1424,20 @@ function registerPublishCommand(program) {
1404
1424
  // Show security review result if available
1405
1425
  const secReview = result.security_review;
1406
1426
  if (secReview?.verdict) {
1407
- if (secReview.verdict === 'passed') {
1427
+ if (secReview.verdict === 'approved') {
1408
1428
  process.stdout.write(`Security: ${chalk_1.default.green('passed')}\n`);
1409
1429
  }
1410
1430
  else if (secReview.verdict === 'flagged') {
1411
1431
  process.stdout.write(`Security: ${chalk_1.default.yellow('flagged')} — ${secReview.summary || 'review recommended'}\n`);
1412
1432
  }
1433
+ else if (secReview.verdict === 'error') {
1434
+ process.stdout.write(`Security: ${chalk_1.default.gray('review unavailable')} — publish succeeded, review will be retried\n`);
1435
+ }
1436
+ else if (secReview.verdict === 'skipped') {
1437
+ process.stdout.write(`Security: ${chalk_1.default.gray('review skipped')} — ${secReview.summary || 'content not eligible for review'}\n`);
1438
+ }
1413
1439
  else {
1414
- process.stdout.write(`Security: ${secReview.verdict}\n`);
1440
+ process.stdout.write(`Security: ${chalk_1.default.gray(secReview.verdict)} — ${secReview.summary || ''}\n`);
1415
1441
  }
1416
1442
  }
1417
1443
  if (result.service_key) {
@@ -12,21 +12,10 @@ const child_process_1 = require("child_process");
12
12
  const config_1 = require("../lib/config");
13
13
  const api_1 = require("../lib/api");
14
14
  const errors_1 = require("../lib/errors");
15
+ const resolve_agent_1 = require("../lib/resolve-agent");
15
16
  const analytics_1 = require("../lib/analytics");
16
17
  const output_1 = require("../lib/output");
17
18
  // ─── Helpers ────────────────────────────────────────────────────────────────
18
- function parsePullRef(value) {
19
- const [ref, versionPart] = value.split('@');
20
- const version = versionPart?.trim() || 'latest';
21
- const segments = ref.split('/');
22
- if (segments.length === 1) {
23
- return { agent: segments[0], version };
24
- }
25
- if (segments.length === 2) {
26
- return { org: segments[0], agent: segments[1], version };
27
- }
28
- throw new errors_1.CliError('Invalid agent reference. Use org/agent[@version] or agent[@version] format.');
29
- }
30
19
  function canonicalType(typeValue) {
31
20
  const normalized = (typeValue || 'agent').toLowerCase();
32
21
  if (['prompt', 'tool', 'agent', 'skill'].includes(normalized))
@@ -292,7 +281,7 @@ function buildManifest(data) {
292
281
  return manifest;
293
282
  }
294
283
  // ─── Bundle Download + Extraction ───────────────────────────────────────────
295
- async function downloadBundle(config, org, agent, version, agentId) {
284
+ async function downloadBundle(config, org, agent, version, agentId, workspaceId) {
296
285
  try {
297
286
  return await (0, api_1.downloadCodeBundle)(config, org, agent, version);
298
287
  }
@@ -305,7 +294,7 @@ async function downloadBundle(config, org, agent, version, agentId) {
305
294
  }
306
295
  if (config.apiKey && agentId) {
307
296
  try {
308
- return await (0, api_1.downloadCodeBundleAuthenticated)(config, agentId);
297
+ return await (0, api_1.downloadCodeBundleAuthenticated)(config, agentId, workspaceId);
309
298
  }
310
299
  catch (err) {
311
300
  if (!(err instanceof api_1.ApiError) || err.status !== 404)
@@ -359,19 +348,12 @@ Examples:
359
348
  process.stdout.write(message);
360
349
  };
361
350
  const config = await (0, config_1.getResolvedConfig)();
362
- const parsed = parsePullRef(agentRef);
363
- // Resolve org from workspace / defaultOrg fallback
364
- const configFile = await (0, config_1.loadConfig)();
365
- const org = parsed.org ?? configFile.workspace ?? config.defaultOrg;
366
- if (!org) {
367
- throw new errors_1.CliError('Missing org. Use org/agent[@version] format, or set a default org with:\n' +
368
- ' orch config set default-org <org>');
369
- }
370
- // Resolve workspace context for the target org
371
- const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
372
- write(`Resolving ${org}/${parsed.agent}@${parsed.version}...\n`);
351
+ const { org, agent: agentName, version, workspaceId } = await (0, resolve_agent_1.resolveAgentContext)(agentRef, config, {
352
+ missingOrgMessage: 'Missing org. Use org/agent[@version] format, or set a default org with:\n orch config set default-org <org>',
353
+ });
354
+ write(`Resolving ${org}/${agentName}@${version}...\n`);
373
355
  // Resolve agent data
374
- const data = await resolveAgent(config, org, parsed.agent, parsed.version, workspaceId);
356
+ const data = await resolveAgent(config, org, agentName, version, workspaceId);
375
357
  // Reject skills
376
358
  if (canonicalType(data.type) === 'skill') {
377
359
  throw new errors_1.CliError("This is a skill. Use 'orch skill install <ref>' instead.");
@@ -411,7 +393,7 @@ Examples:
411
393
  // and exit-code 1.
412
394
  if (engine === 'code_runtime' && data.has_bundle) {
413
395
  write('Downloading code bundle...\n');
414
- const bundle = await downloadBundle(config, org, data.name, data.version, data.agentId);
396
+ const bundle = await downloadBundle(config, org, data.name, data.version, data.agentId, workspaceId);
415
397
  if (bundle) {
416
398
  const tempDir = path_1.default.join(os_1.default.tmpdir(), `orchagent-pull-${Date.now()}`);
417
399
  const zipPath = path_1.default.join(tempDir, 'bundle.zip');
@@ -455,7 +437,7 @@ Examples:
455
437
  // Track analytics
456
438
  await (0, analytics_1.track)('cli_pull', {
457
439
  org,
458
- agent: parsed.agent,
440
+ agent: agentName,
459
441
  version: data.version,
460
442
  engine,
461
443
  source: data.source,
@@ -465,7 +447,7 @@ Examples:
465
447
  if (options.json) {
466
448
  const result = {
467
449
  success: true,
468
- requested_ref: `${org}/${parsed.agent}@${parsed.version}`,
450
+ requested_ref: `${org}/${agentName}@${version}`,
469
451
  resolved_ref: resolvedRef,
470
452
  output_dir: outputDir,
471
453
  engine,
@@ -91,6 +91,21 @@ function registerReplayCommand(program) {
91
91
  .option('--override-policy <id>', 'Override provider policy ID for this replay')
92
92
  .option('--no-wait', 'Queue the replay and return immediately without waiting for results')
93
93
  .option('--json', 'Output as JSON')
94
+ .addHelpText('after', `
95
+ How snapshots work:
96
+ Every cloud run automatically captures a snapshot of its input, config,
97
+ secrets, and agent version before execution. This snapshot enables
98
+ deterministic replay — even if the agent has been updated since.
99
+
100
+ Local runs (--local) do not create snapshots and cannot be replayed.
101
+ Runs created before snapshot support was added also cannot be replayed.
102
+
103
+ Examples:
104
+ orch replay a1b2c3d4 Replay and wait for results
105
+ orch replay a1b2c3d4 --no-wait Queue and return immediately
106
+ orch replay a1b2c3d4 --reason "debug" Replay with audit reason
107
+ orch replay a1b2c3d4 --json Output as JSON
108
+ `)
94
109
  .action(async (runId, options) => {
95
110
  const config = await (0, config_1.getResolvedConfig)();
96
111
  if (!config.apiKey) {
@@ -109,15 +124,33 @@ function registerReplayCommand(program) {
109
124
  throw new errors_1.CliError(`Invalid run ID '${runId}'. Provide a full UUID or a short hex prefix (7+ characters).`);
110
125
  }
111
126
  // Submit replay request
112
- const body = {};
127
+ const reqBody = {};
113
128
  if (options.reason)
114
- body.reason = options.reason;
129
+ reqBody.reason = options.reason;
115
130
  if (options.overridePolicy)
116
- body.override_provider_policy_id = options.overridePolicy;
117
- const replay = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/runs/${resolvedRunId}/replay`, {
118
- body: JSON.stringify(body),
119
- headers: { 'Content-Type': 'application/json' },
120
- });
131
+ reqBody.override_provider_policy_id = options.overridePolicy;
132
+ let replay;
133
+ try {
134
+ replay = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/runs/${resolvedRunId}/replay`, {
135
+ body: JSON.stringify(reqBody),
136
+ headers: { 'Content-Type': 'application/json' },
137
+ });
138
+ }
139
+ catch (err) {
140
+ if (err instanceof api_1.ApiError && err.status === 404) {
141
+ const isSnapshotMissing = err.message.toLowerCase().includes('snapshot');
142
+ if (isSnapshotMissing) {
143
+ throw new errors_1.CliError(`No snapshot available for run ${resolvedRunId.slice(0, 8)}.\n\n` +
144
+ `Snapshots are captured automatically for cloud runs. This run may have been:\n` +
145
+ ` - A local run (--local), which does not create snapshots\n` +
146
+ ` - Created before snapshot support was added\n\n` +
147
+ `Run \`orch logs ${resolvedRunId.slice(0, 8)}\` to inspect the original run.`);
148
+ }
149
+ throw new errors_1.CliError(`Run ${resolvedRunId.slice(0, 8)} not found. ` +
150
+ `Check the run ID and workspace, or run \`orch logs\` to list recent runs.`);
151
+ }
152
+ throw err;
153
+ }
121
154
  if (options.json && options.wait === false) {
122
155
  (0, output_1.printJson)(replay);
123
156
  return;