@orchagent/cli 0.3.54 → 0.3.55

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.
@@ -29,6 +29,8 @@ const security_1 = require("./security");
29
29
  const billing_1 = require("./billing");
30
30
  const agent_keys_1 = require("./agent-keys");
31
31
  const schedule_1 = require("./schedule");
32
+ const service_1 = require("./service");
33
+ const transfer_1 = require("./transfer");
32
34
  function registerCommands(program) {
33
35
  (0, login_1.registerLoginCommand)(program);
34
36
  (0, whoami_1.registerWhoamiCommand)(program);
@@ -58,4 +60,6 @@ function registerCommands(program) {
58
60
  (0, billing_1.registerBillingCommand)(program);
59
61
  (0, agent_keys_1.registerAgentKeysCommand)(program);
60
62
  (0, schedule_1.registerScheduleCommand)(program);
63
+ (0, service_1.registerServiceCommand)(program);
64
+ (0, transfer_1.registerTransferCommand)(program);
61
65
  }
@@ -10,7 +10,8 @@ const errors_1 = require("../lib/errors");
10
10
  const MANIFEST_TEMPLATE = `{
11
11
  "name": "my-agent",
12
12
  "description": "A simple AI agent",
13
- "type": "prompt",
13
+ "type": "agent",
14
+ "run_mode": "on_demand",
14
15
  "tags": []
15
16
  }
16
17
  `;
@@ -81,13 +82,15 @@ def main():
81
82
  if __name__ == "__main__":
82
83
  main()
83
84
  `;
84
- function readmeTemplate(agentName, type) {
85
- const cloudExample = type === 'tool'
86
- ? `orchagent run ${agentName} input-file.txt`
87
- : `orchagent run ${agentName} --data '{"${type === 'agent' ? 'task' : 'input'}": "Hello world"}'`;
88
- const localExample = type === 'tool'
89
- ? `orchagent run ${agentName} --local --data '{"file_path": "src/app.py"}'`
90
- : `orchagent run ${agentName} --local --data '{"${type === 'agent' ? 'task' : 'input'}": "Hello world"}'`;
85
+ function readmeTemplate(agentName, flavor) {
86
+ const inputField = flavor === 'managed_loop' ? 'task' : 'input';
87
+ const inputDescription = flavor === 'managed_loop' ? 'The task to perform' : 'The input to process';
88
+ const cloudExample = flavor === 'code_runtime'
89
+ ? `orchagent run ${agentName} --data '{"input": "Hello world"}'`
90
+ : `orchagent run ${agentName} --data '{"${inputField}": "Hello world"}'`;
91
+ const localExample = flavor === 'code_runtime'
92
+ ? `orchagent run ${agentName} --local --data '{"input": "Hello world"}'`
93
+ : `orchagent run ${agentName} --local --data '{"${inputField}": "Hello world"}'`;
91
94
  return `# ${agentName}
92
95
 
93
96
  A brief description of what this agent does.
@@ -110,7 +113,7 @@ ${localExample}
110
113
 
111
114
  | Field | Type | Description |
112
115
  |-------|------|-------------|
113
- | \`${type === 'agent' ? 'task' : 'input'}\` | string | ${type === 'agent' ? 'The task to perform' : 'The input to process'} |
116
+ | \`${inputField}\` | string | ${inputDescription} |
114
117
 
115
118
  ## Output
116
119
 
@@ -119,14 +122,6 @@ ${localExample}
119
122
  | \`result\` | string | The agent's response |
120
123
  `;
121
124
  }
122
- const AGENT_MANIFEST_TEMPLATE = `{
123
- "name": "my-agent",
124
- "description": "An AI agent with tool use",
125
- "type": "agent",
126
- "supported_providers": ["anthropic"],
127
- "max_turns": 25
128
- }
129
- `;
130
125
  const AGENT_PROMPT_TEMPLATE = `You are a helpful AI agent.
131
126
 
132
127
  Given the input, complete the task step by step.
@@ -169,14 +164,39 @@ license: MIT
169
164
 
170
165
  Instructions and guidance for AI agents...
171
166
  `;
167
+ function resolveInitFlavor(typeOption) {
168
+ const normalized = (typeOption || 'agent').trim().toLowerCase();
169
+ if (normalized === 'skill') {
170
+ return { type: 'skill' };
171
+ }
172
+ if (normalized === 'agent') {
173
+ return { type: 'agent', flavor: 'direct_llm' };
174
+ }
175
+ if (normalized === 'prompt') {
176
+ return { type: 'agent', flavor: 'direct_llm' };
177
+ }
178
+ if (normalized === 'agentic') {
179
+ return { type: 'agent', flavor: 'managed_loop' };
180
+ }
181
+ if (normalized === 'tool' || normalized === 'code') {
182
+ return { type: 'agent', flavor: 'code_runtime' };
183
+ }
184
+ throw new errors_1.CliError(`Unknown --type '${typeOption}'. Use 'agent' or 'skill' (legacy: prompt, tool, agentic, code).`);
185
+ }
172
186
  function registerInitCommand(program) {
173
187
  program
174
188
  .command('init')
175
189
  .description('Initialize a new agent project')
176
190
  .argument('[name]', 'Agent name (default: current directory name)')
177
- .option('--type <type>', 'Type: prompt, tool, agent, or skill (default: prompt)', 'prompt')
191
+ .option('--type <type>', 'Type: agent or skill (legacy: prompt, tool, agentic, code)', 'agent')
192
+ .option('--run-mode <mode>', 'Run mode for agents: on_demand or always_on', 'on_demand')
178
193
  .action(async (name, options) => {
179
194
  const cwd = process.cwd();
195
+ const runMode = (options.runMode || 'on_demand').trim().toLowerCase();
196
+ if (!['on_demand', 'always_on'].includes(runMode)) {
197
+ throw new errors_1.CliError("Invalid --run-mode. Use 'on_demand' or 'always_on'.");
198
+ }
199
+ const initMode = resolveInitFlavor(options.type);
180
200
  // When a name is provided, create a subdirectory for the project
181
201
  const targetDir = name ? path_1.default.join(cwd, name) : cwd;
182
202
  const agentName = name || path_1.default.basename(cwd);
@@ -185,7 +205,7 @@ function registerInitCommand(program) {
185
205
  await promises_1.default.mkdir(targetDir, { recursive: true });
186
206
  }
187
207
  // Handle skill type separately
188
- if (options.type === 'skill') {
208
+ if (initMode.type === 'skill') {
189
209
  const skillPath = path_1.default.join(targetDir, 'SKILL.md');
190
210
  // Check if already initialized
191
211
  try {
@@ -227,38 +247,45 @@ function registerInitCommand(program) {
227
247
  throw err;
228
248
  }
229
249
  }
250
+ if (initMode.flavor === 'direct_llm' && runMode === 'always_on') {
251
+ throw new errors_1.CliError("run_mode=always_on requires a non-direct runtime. Use legacy '--type tool' or add runtime.command in orchagent.json.");
252
+ }
230
253
  // Create manifest and type-specific files
231
- if (options.type === 'agent') {
232
- const manifest = JSON.parse(AGENT_MANIFEST_TEMPLATE);
233
- manifest.name = agentName;
234
- await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
254
+ const manifest = JSON.parse(MANIFEST_TEMPLATE);
255
+ manifest.name = agentName;
256
+ manifest.type = 'agent';
257
+ manifest.run_mode = runMode;
258
+ if (initMode.flavor === 'managed_loop') {
259
+ manifest.description = 'An AI agent with tool use';
260
+ manifest.supported_providers = ['anthropic'];
261
+ manifest.loop = { max_turns: 25 };
262
+ }
263
+ else if (initMode.flavor === 'code_runtime') {
264
+ manifest.description = 'A code-runtime agent';
265
+ manifest.runtime = { command: 'python main.py' };
266
+ }
267
+ await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
268
+ if (initMode.flavor === 'code_runtime') {
269
+ const entrypointPath = path_1.default.join(targetDir, 'main.py');
270
+ await promises_1.default.writeFile(entrypointPath, CODE_TEMPLATE_PY);
271
+ await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
272
+ }
273
+ else if (initMode.flavor === 'managed_loop') {
235
274
  await promises_1.default.writeFile(promptPath, AGENT_PROMPT_TEMPLATE);
236
275
  await promises_1.default.writeFile(schemaPath, AGENT_SCHEMA_TEMPLATE);
237
276
  }
238
277
  else {
239
- const manifest = JSON.parse(MANIFEST_TEMPLATE);
240
- manifest.name = agentName;
241
- manifest.type = ['tool', 'skill'].includes(options.type) ? options.type : 'prompt';
242
- await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
243
- // Create prompt template (for prompt-based agents) or entrypoint (for tool agents)
244
- if (options.type === 'tool') {
245
- const entrypointPath = path_1.default.join(targetDir, 'main.py');
246
- await promises_1.default.writeFile(entrypointPath, CODE_TEMPLATE_PY);
247
- }
248
- else {
249
- await promises_1.default.writeFile(promptPath, PROMPT_TEMPLATE);
250
- }
251
- // Create schema template
278
+ await promises_1.default.writeFile(promptPath, PROMPT_TEMPLATE);
252
279
  await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
253
280
  }
254
281
  // Create README
255
282
  const readmePath = path_1.default.join(targetDir, 'README.md');
256
- await promises_1.default.writeFile(readmePath, readmeTemplate(agentName, options.type));
283
+ await promises_1.default.writeFile(readmePath, readmeTemplate(agentName, initMode.flavor || 'direct_llm'));
257
284
  process.stdout.write(`Initialized agent "${agentName}" in ${targetDir}\n`);
258
285
  process.stdout.write(`\nFiles created:\n`);
259
286
  const prefix = name ? name + '/' : '';
260
287
  process.stdout.write(` ${prefix}orchagent.json - Agent configuration\n`);
261
- if (options.type === 'tool') {
288
+ if (initMode.flavor === 'code_runtime') {
262
289
  process.stdout.write(` ${prefix}main.py - Agent entrypoint (stdin/stdout JSON)\n`);
263
290
  }
264
291
  else {
@@ -266,32 +293,26 @@ function registerInitCommand(program) {
266
293
  }
267
294
  process.stdout.write(` ${prefix}schema.json - Input/output schemas\n`);
268
295
  process.stdout.write(` ${prefix}README.md - Agent documentation\n`);
296
+ process.stdout.write(` Run mode: ${runMode}\n`);
297
+ process.stdout.write(` Execution: ${initMode.flavor}\n`);
269
298
  process.stdout.write(`\nNext steps:\n`);
270
- if (options.type === 'agent') {
271
- const stepNum = name ? 2 : 1;
272
- if (name) {
273
- process.stdout.write(` 1. cd ${name}\n`);
274
- }
275
- process.stdout.write(` ${stepNum}. Edit prompt.md with your agent instructions\n`);
276
- process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
277
- process.stdout.write(` ${stepNum + 2}. Run: orchagent publish\n`);
278
- }
279
- else if (options.type !== 'tool') {
299
+ if (initMode.flavor === 'code_runtime') {
280
300
  const stepNum = name ? 2 : 1;
281
301
  if (name) {
282
302
  process.stdout.write(` 1. cd ${name}\n`);
283
303
  }
284
- process.stdout.write(` ${stepNum}. Edit prompt.md with your prompt template\n`);
304
+ process.stdout.write(` ${stepNum}. Edit main.py with your agent logic\n`);
285
305
  process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
286
- process.stdout.write(` ${stepNum + 2}. Run: orchagent publish\n`);
306
+ process.stdout.write(` ${stepNum + 2}. Test: echo '{"input": "test"}' | python main.py\n`);
307
+ process.stdout.write(` ${stepNum + 3}. Run: orchagent publish\n`);
287
308
  }
288
309
  else {
289
310
  const stepNum = name ? 2 : 1;
290
311
  if (name) {
291
312
  process.stdout.write(` 1. cd ${name}\n`);
292
313
  }
293
- process.stdout.write(` ${stepNum}. Edit main.py with your agent logic\n`);
294
- process.stdout.write(` ${stepNum + 1}. Test: echo '{"input": "test"}' | python main.py\n`);
314
+ process.stdout.write(` ${stepNum}. Edit prompt.md with your agent instructions\n`);
315
+ process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
295
316
  process.stdout.write(` ${stepNum + 2}. Run: orchagent publish\n`);
296
317
  }
297
318
  });
@@ -161,6 +161,48 @@ async function collectSkillFiles(skillDir, maxFiles = 20, maxTotalSize = 500_000
161
161
  await walkDir(skillDir);
162
162
  return files;
163
163
  }
164
+ function canonicalizeManifestType(typeValue) {
165
+ const rawType = (typeValue || 'agent').trim().toLowerCase();
166
+ if (rawType === 'skill') {
167
+ return { canonicalType: 'skill', rawType };
168
+ }
169
+ if (['agent', 'prompt', 'tool', 'agentic', 'code'].includes(rawType)) {
170
+ return { canonicalType: 'agent', rawType };
171
+ }
172
+ throw new errors_1.CliError(`Invalid type '${typeValue}'. Use 'agent' or 'skill' (legacy values accepted: prompt, tool, agentic, code).`);
173
+ }
174
+ function normalizeRunMode(runMode) {
175
+ const normalized = (runMode || 'on_demand').trim().toLowerCase();
176
+ if (normalized === 'on_demand' || normalized === 'always_on') {
177
+ return normalized;
178
+ }
179
+ throw new errors_1.CliError("run_mode must be 'on_demand' or 'always_on'");
180
+ }
181
+ function inferExecutionEngineFromManifest(manifest, rawType) {
182
+ const runtimeCommand = manifest.runtime?.command?.trim();
183
+ const hasLoop = Boolean(manifest.loop && Object.keys(manifest.loop).length > 0);
184
+ if (runtimeCommand && hasLoop) {
185
+ throw new errors_1.CliError('runtime.command and loop cannot both be set');
186
+ }
187
+ if (runtimeCommand)
188
+ return 'code_runtime';
189
+ if (hasLoop)
190
+ return 'managed_loop';
191
+ if (rawType === 'tool' || rawType === 'code')
192
+ return 'code_runtime';
193
+ if (rawType === 'agentic')
194
+ return 'managed_loop';
195
+ if (rawType === 'agent' && (manifest.custom_tools?.length || manifest.max_turns)) {
196
+ return 'managed_loop';
197
+ }
198
+ return 'direct_llm';
199
+ }
200
+ function commandForEntrypoint(entrypoint) {
201
+ if (entrypoint.endsWith('.js') || entrypoint.endsWith('.mjs') || entrypoint.endsWith('.cjs') || entrypoint.endsWith('.ts')) {
202
+ return `node ${entrypoint}`;
203
+ }
204
+ return `python ${entrypoint}`;
205
+ }
164
206
  function registerPublishCommand(program) {
165
207
  program
166
208
  .command('publish')
@@ -269,6 +311,16 @@ function registerPublishCommand(program) {
269
311
  if (!manifest.name) {
270
312
  throw new errors_1.CliError('orchagent.json must have name');
271
313
  }
314
+ const { canonicalType, rawType } = canonicalizeManifestType(manifest.type);
315
+ const runMode = normalizeRunMode(manifest.run_mode);
316
+ const executionEngine = inferExecutionEngineFromManifest(manifest, rawType);
317
+ const callable = Boolean(manifest.callable);
318
+ if (canonicalType === 'skill') {
319
+ throw new errors_1.CliError("Use SKILL.md for publishing skills. Remove orchagent.json and run 'orchagent publish' from a skill directory.");
320
+ }
321
+ if (runMode === 'always_on' && executionEngine === 'direct_llm') {
322
+ throw new errors_1.CliError('run_mode=always_on requires runtime.command or loop configuration');
323
+ }
272
324
  // Warn about deprecated prompt field
273
325
  if (manifest.prompt) {
274
326
  process.stderr.write(chalk_1.default.yellow('Warning: "prompt" field in orchagent.json is ignored. Use prompt.md file instead.\n'));
@@ -310,7 +362,7 @@ function registerPublishCommand(program) {
310
362
  `These must be nested under a "manifest" key. Example:\n\n` +
311
363
  ` {\n` +
312
364
  ` "name": "${manifest.name}",\n` +
313
- ` "type": "${manifest.type || 'tool'}",\n` +
365
+ ` "type": "${manifest.type || 'agent'}",\n` +
314
366
  ` "manifest": {\n` +
315
367
  ` "manifest_version": 1,\n` +
316
368
  ` "dependencies": [...],\n` +
@@ -321,26 +373,25 @@ function registerPublishCommand(program) {
321
373
  ` }\n\n` +
322
374
  `See docs/manifest.md for details.`);
323
375
  }
324
- // Read prompt (for prompt-based, agent, and skill agents)
376
+ // Read prompt for LLM-driven engines (direct_llm + managed_loop).
325
377
  let prompt;
326
- if (manifest.type === 'prompt' || manifest.type === 'skill' || manifest.type === 'agent') {
378
+ if (executionEngine === 'direct_llm' || executionEngine === 'managed_loop') {
327
379
  const promptPath = path_1.default.join(cwd, 'prompt.md');
328
380
  try {
329
381
  prompt = await promises_1.default.readFile(promptPath, 'utf-8');
330
382
  }
331
383
  catch (err) {
332
384
  if (err.code === 'ENOENT') {
333
- const agentTypeName = manifest.type === 'skill' ? 'skill' : manifest.type === 'agent' ? 'agent' : 'prompt-based agent';
334
- throw new errors_1.CliError(`No prompt.md found for ${agentTypeName}.\n\n` +
385
+ throw new errors_1.CliError('No prompt.md found for this agent.\n\n' +
335
386
  'Create a prompt.md file in the current directory with your prompt template.\n' +
336
387
  'See: https://orchagent.io/docs/publishing');
337
388
  }
338
389
  throw err;
339
390
  }
340
391
  }
341
- // For agent type, validate custom_tools and build manifest
342
- if (manifest.type === 'agent') {
343
- // Validate custom_tools format
392
+ // Validate managed-loop specific fields + normalize loop payload
393
+ let loopConfig;
394
+ if (executionEngine === 'managed_loop') {
344
395
  if (manifest.custom_tools) {
345
396
  const reservedNames = new Set(['bash', 'read_file', 'write_file', 'list_files', 'submit_result']);
346
397
  const seenNames = new Set();
@@ -359,24 +410,24 @@ function registerPublishCommand(program) {
359
410
  seenNames.add(tool.name);
360
411
  }
361
412
  }
362
- // Validate max_turns
363
413
  if (manifest.max_turns !== undefined) {
364
414
  if (typeof manifest.max_turns !== 'number' || manifest.max_turns < 1 || manifest.max_turns > 50) {
365
415
  throw new errors_1.CliError('max_turns must be a number between 1 and 50');
366
416
  }
367
417
  }
368
- // Store agent config in manifest field
369
- const agentManifest = {
370
- ...(manifest.manifest || {}),
371
- };
372
- if (manifest.custom_tools) {
373
- agentManifest.custom_tools = manifest.custom_tools;
418
+ const providedLoop = manifest.loop && typeof manifest.loop === 'object'
419
+ ? { ...manifest.loop }
420
+ : {};
421
+ if (!('max_turns' in providedLoop) && manifest.max_turns !== undefined) {
422
+ providedLoop.max_turns = manifest.max_turns;
423
+ }
424
+ if (!('custom_tools' in providedLoop) && manifest.custom_tools?.length) {
425
+ providedLoop.custom_tools = manifest.custom_tools;
374
426
  }
375
- if (manifest.max_turns) {
376
- agentManifest.max_turns = manifest.max_turns;
427
+ if (Object.keys(providedLoop).length === 0) {
428
+ providedLoop.max_turns = 25;
377
429
  }
378
- manifest.manifest = agentManifest;
379
- // Agent type defaults to anthropic provider
430
+ loopConfig = providedLoop;
380
431
  if (!manifest.supported_providers) {
381
432
  manifest.supported_providers = ['anthropic'];
382
433
  }
@@ -398,9 +449,8 @@ function registerPublishCommand(program) {
398
449
  throw new errors_1.CliError(`Failed to read schema.json: ${err}`);
399
450
  }
400
451
  }
401
- // For prompt/skill agents, derive input schema from template variables if needed
402
- // (Agent type uses schema.json directly no template variable derivation)
403
- if (prompt && (manifest.type === 'prompt' || manifest.type === 'skill')) {
452
+ // For direct LLM and managed loop agents, derive input schema from template variables if needed.
453
+ if (prompt && (executionEngine === 'direct_llm' || executionEngine === 'managed_loop')) {
404
454
  const templateVars = extractTemplateVariables(prompt);
405
455
  if (templateVars.length > 0) {
406
456
  if (!schemaFromFile) {
@@ -426,47 +476,44 @@ function registerPublishCommand(program) {
426
476
  }
427
477
  }
428
478
  }
429
- // For tool-based agents, either --url is required OR we bundle the code
430
- // For agent type, use internal placeholder (no user code, platform handles execution)
431
479
  let agentUrl = options.url;
432
480
  let shouldUploadBundle = false;
433
- if (manifest.type === 'agent') {
434
- // Agent type doesn't need a URL or code bundle
435
- agentUrl = agentUrl || 'https://agent.internal';
436
- // But they can include a Dockerfile for custom environments
437
- if (options.docker) {
438
- const dockerfilePath = path_1.default.join(cwd, 'Dockerfile');
439
- try {
440
- await promises_1.default.access(dockerfilePath);
441
- shouldUploadBundle = true;
442
- process.stdout.write(`Including Dockerfile for custom environment\n`);
481
+ let runtimeConfig;
482
+ let bundleEntrypoint = manifest.entrypoint;
483
+ if (executionEngine === 'code_runtime') {
484
+ if (!bundleEntrypoint) {
485
+ bundleEntrypoint = await (0, bundle_1.detectEntrypoint)(cwd) || undefined;
486
+ }
487
+ if (!options.url) {
488
+ if (!bundleEntrypoint) {
489
+ throw new errors_1.CliError('Tool requires either --url <url> or an entry point file (main.py, app.py, index.js, etc.)');
443
490
  }
444
- catch {
445
- throw new errors_1.CliError('--docker flag specified but no Dockerfile found in project directory');
446
- }
447
- }
448
- }
449
- else if (manifest.type === 'tool' && !options.url) {
450
- // Check if this looks like a Python or JS project that can be bundled
451
- const entrypoint = manifest.entrypoint || await (0, bundle_1.detectEntrypoint)(cwd);
452
- if (entrypoint) {
453
- // This is a hosted tool - we'll bundle and upload
454
491
  shouldUploadBundle = true;
455
- // Set a placeholder URL that tells the gateway to use sandbox execution
456
492
  agentUrl = 'https://tool.internal';
457
- process.stdout.write(`Detected tool project with entrypoint: ${entrypoint}\n`);
493
+ process.stdout.write(`Detected code runtime entrypoint: ${bundleEntrypoint}\n`);
458
494
  }
459
- else {
460
- throw new errors_1.CliError('Tool requires either --url <url> or an entry point file (main.py, app.py, index.js, etc.)');
495
+ let runtimeCommand = manifest.runtime?.command?.trim() || '';
496
+ if (!runtimeCommand && manifest.run_command?.trim()) {
497
+ runtimeCommand = manifest.run_command.trim();
498
+ }
499
+ if (!runtimeCommand) {
500
+ runtimeCommand = commandForEntrypoint(bundleEntrypoint || 'main.py');
461
501
  }
502
+ runtimeConfig = { ...(manifest.runtime || {}), command: runtimeCommand };
503
+ }
504
+ else {
505
+ agentUrl = agentUrl || 'https://prompt-agent.internal';
506
+ }
507
+ if (options.docker && executionEngine !== 'code_runtime') {
508
+ throw new errors_1.CliError('--docker is only supported for code runtime agents');
462
509
  }
463
510
  // Get org info
464
511
  const org = await (0, api_1.getOrg)(config);
465
512
  // Default to 'any' provider if not specified
466
513
  const supportedProviders = manifest.supported_providers || ['any'];
467
- // Detect SDK compatibility for tool agents
514
+ // Detect SDK compatibility for code runtime agents
468
515
  let sdkCompatible = false;
469
- if (manifest.type === 'tool') {
516
+ if (executionEngine === 'code_runtime') {
470
517
  sdkCompatible = await detectSdkCompatible(cwd);
471
518
  if (sdkCompatible && !options.dryRun) {
472
519
  process.stdout.write(`SDK detected - agent will be marked as Local Ready\n`);
@@ -481,8 +528,7 @@ function registerPublishCommand(program) {
481
528
  process.stderr.write('\nDRY RUN - No changes will be made\n\n');
482
529
  process.stderr.write('Validating...\n');
483
530
  process.stderr.write(` ✓ orchagent.json found and valid\n`);
484
- if (manifest.type === 'prompt') {
485
- // Prompt agent validations
531
+ if (executionEngine === 'direct_llm') {
486
532
  const promptBytes = prompt ? Buffer.byteLength(prompt, 'utf-8') : 0;
487
533
  process.stderr.write(` ✓ prompt.md found (${promptBytes.toLocaleString()} bytes)\n`);
488
534
  if (schemaFromFile) {
@@ -494,31 +540,29 @@ function registerPublishCommand(program) {
494
540
  process.stderr.write(` ✓ Input schema derived from template variables: ${vars.join(', ')}\n`);
495
541
  }
496
542
  }
497
- else if (manifest.type === 'agent') {
498
- // Agent type validations
543
+ else if (executionEngine === 'managed_loop') {
499
544
  const promptBytes = prompt ? Buffer.byteLength(prompt, 'utf-8') : 0;
500
545
  process.stderr.write(` ✓ prompt.md found (${promptBytes.toLocaleString()} bytes)\n`);
501
546
  if (schemaFromFile) {
502
547
  const schemaTypes = [inputSchema ? 'input' : null, outputSchema ? 'output' : null].filter(Boolean).join(' + ');
503
548
  process.stderr.write(` ✓ schema.json found (${schemaTypes} schemas)\n`);
504
549
  }
505
- const customToolCount = manifest.custom_tools?.length || 0;
550
+ const customToolCount = manifest.custom_tools?.length || Number(Array.isArray(loopConfig?.custom_tools) ? loopConfig.custom_tools.length : 0);
506
551
  process.stderr.write(` ✓ Custom tools: ${customToolCount}\n`);
507
- process.stderr.write(` ✓ Max turns: ${manifest.max_turns || 25}\n`);
552
+ process.stderr.write(` ✓ Max turns: ${loopConfig?.max_turns || manifest.max_turns || 25}\n`);
508
553
  }
509
- else if (manifest.type === 'tool') {
510
- // Tool agent validations
511
- const entrypoint = manifest.entrypoint || await (0, bundle_1.detectEntrypoint)(cwd);
512
- process.stderr.write(` ✓ Entrypoint: ${entrypoint}\n`);
554
+ else if (executionEngine === 'code_runtime') {
555
+ process.stderr.write(` ✓ runtime.command: ${String(runtimeConfig?.command || '')}\n`);
556
+ process.stderr.write(` ✓ Entrypoint: ${bundleEntrypoint || '(remote url only)'}\n`);
513
557
  if (sdkCompatible) {
514
558
  process.stderr.write(` ✓ SDK detected (orchagent-sdk in requirements.txt)\n`);
515
559
  }
516
560
  }
517
561
  process.stderr.write(` ✓ Authentication valid (org: ${org.slug})\n`);
518
- // For tools with bundles, show bundle preview
562
+ // For hosted code-runtime agents, show bundle preview
519
563
  if (shouldUploadBundle) {
520
564
  const bundlePreview = await (0, bundle_1.previewBundle)(cwd, {
521
- entrypoint: manifest.entrypoint,
565
+ entrypoint: bundleEntrypoint,
522
566
  exclude: manifest.bundle?.exclude,
523
567
  include: manifest.bundle?.include,
524
568
  });
@@ -529,7 +573,10 @@ function registerPublishCommand(program) {
529
573
  }
530
574
  process.stderr.write('\nAgent Preview:\n');
531
575
  process.stderr.write(` Name: ${manifest.name}\n`);
532
- process.stderr.write(` Type: ${manifest.type}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
576
+ process.stderr.write(` Type: ${canonicalType}\n`);
577
+ process.stderr.write(` Run mode: ${runMode}\n`);
578
+ process.stderr.write(` Engine: ${executionEngine}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
579
+ process.stderr.write(` Callable: ${callable ? 'enabled' : 'disabled'}\n`);
533
580
  process.stderr.write(` Version: ${versionInfo}\n`);
534
581
  process.stderr.write(` Visibility: private\n`);
535
582
  process.stderr.write(` Providers: ${supportedProviders.join(', ')}\n`);
@@ -544,7 +591,7 @@ function registerPublishCommand(program) {
544
591
  process.stderr.write(`\nWould publish: ${preview.org_slug}/${manifest.name}@${preview.next_version}\n`);
545
592
  if (shouldUploadBundle) {
546
593
  const bundlePreview = await (0, bundle_1.previewBundle)(cwd, {
547
- entrypoint: manifest.entrypoint,
594
+ entrypoint: bundleEntrypoint,
548
595
  exclude: manifest.bundle?.exclude,
549
596
  include: manifest.bundle?.include,
550
597
  });
@@ -559,7 +606,11 @@ function registerPublishCommand(program) {
559
606
  try {
560
607
  result = await (0, api_1.createAgent)(config, {
561
608
  name: manifest.name,
562
- type: manifest.type,
609
+ type: canonicalType,
610
+ run_mode: runMode,
611
+ runtime: runtimeConfig,
612
+ loop: loopConfig,
613
+ callable,
563
614
  description: manifest.description,
564
615
  prompt,
565
616
  url: agentUrl,
@@ -569,7 +620,7 @@ function registerPublishCommand(program) {
569
620
  is_public: false,
570
621
  supported_providers: supportedProviders,
571
622
  default_models: manifest.default_models,
572
- // Local run fields for tool agents
623
+ // Local run fields for code runtime agents
573
624
  source_url: manifest.source_url,
574
625
  pip_package: manifest.pip_package,
575
626
  run_command: manifest.run_command,
@@ -615,7 +666,7 @@ function registerPublishCommand(program) {
615
666
  }
616
667
  const assignedVersion = result.agent?.version || 'v1';
617
668
  const agentId = result.agent?.id;
618
- // Upload code bundle if this is a hosted tool agent or agent type with --docker
669
+ // Upload code bundle for hosted code runtime agents
619
670
  if (shouldUploadBundle && agentId) {
620
671
  process.stdout.write(`\nBundling code...\n`);
621
672
  const tempDir = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'orchagent-bundle-'));
@@ -634,8 +685,7 @@ function registerPublishCommand(program) {
634
685
  throw new errors_1.CliError('--docker flag specified but no Dockerfile found in project directory');
635
686
  }
636
687
  }
637
- // For agent type, also include requirements.txt if present
638
- if (manifest.type === 'agent') {
688
+ if (executionEngine === 'code_runtime') {
639
689
  const reqPath = path_1.default.join(cwd, 'requirements.txt');
640
690
  try {
641
691
  await promises_1.default.access(reqPath);
@@ -647,10 +697,10 @@ function registerPublishCommand(program) {
647
697
  }
648
698
  }
649
699
  const bundleResult = await (0, bundle_1.createCodeBundle)(cwd, bundlePath, {
650
- entrypoint: manifest.type === 'agent' ? undefined : manifest.entrypoint,
700
+ entrypoint: bundleEntrypoint,
651
701
  exclude: manifest.bundle?.exclude,
652
702
  include: includePatterns.length > 0 ? includePatterns : undefined,
653
- skipEntrypointCheck: manifest.type === 'agent',
703
+ skipEntrypointCheck: false,
654
704
  });
655
705
  process.stdout.write(` Created bundle: ${bundleResult.fileCount} files, ${(bundleResult.sizeBytes / 1024).toFixed(1)}KB\n`);
656
706
  // Validate bundle size
@@ -660,7 +710,7 @@ function registerPublishCommand(program) {
660
710
  }
661
711
  // Upload the bundle with entrypoint
662
712
  process.stdout.write(` Uploading bundle...\n`);
663
- const uploadResult = await (0, api_1.uploadCodeBundle)(config, agentId, bundlePath, manifest.entrypoint);
713
+ const uploadResult = await (0, api_1.uploadCodeBundle)(config, agentId, bundlePath, bundleEntrypoint);
664
714
  process.stdout.write(` Uploaded: ${uploadResult.code_hash.substring(0, 12)}...\n`);
665
715
  // Show environment info if applicable
666
716
  if (uploadResult.environment_id) {
@@ -682,9 +732,18 @@ function registerPublishCommand(program) {
682
732
  await promises_1.default.rm(tempDir, { recursive: true, force: true });
683
733
  }
684
734
  }
685
- await (0, analytics_1.track)('cli_publish', { agent_type: manifest.type, hosted: shouldUploadBundle });
735
+ await (0, analytics_1.track)('cli_publish', {
736
+ agent_type: canonicalType,
737
+ execution_engine: executionEngine,
738
+ run_mode: runMode,
739
+ callable,
740
+ hosted: shouldUploadBundle,
741
+ });
686
742
  process.stdout.write(`\nPublished agent: ${org.slug}/${manifest.name}@${assignedVersion}\n`);
687
- process.stdout.write(`Type: ${manifest.type}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
743
+ process.stdout.write(`Type: ${canonicalType}\n`);
744
+ process.stdout.write(`Run mode: ${runMode}\n`);
745
+ process.stdout.write(`Execution engine: ${executionEngine}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
746
+ process.stdout.write(`Callable: ${callable ? 'enabled' : 'disabled'}\n`);
688
747
  process.stdout.write(`Providers: ${supportedProviders.join(', ')}\n`);
689
748
  process.stdout.write(`Visibility: private\n`);
690
749
  // Show security review result if available