@autohq/cli 0.1.133 → 0.1.135

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/dist/index.js CHANGED
@@ -334,8 +334,8 @@ async function createOAuthLoopbackCallback(input) {
334
334
  });
335
335
  let resolveResult;
336
336
  let rejectResult;
337
- const result = new Promise((resolve2, reject) => {
338
- resolveResult = resolve2;
337
+ const result = new Promise((resolve3, reject) => {
338
+ resolveResult = resolve3;
339
339
  rejectResult = reject;
340
340
  });
341
341
  const server = createServer((request, response) => {
@@ -595,14 +595,14 @@ async function listenOnPreferredPort(server, port) {
595
595
  }
596
596
  }
597
597
  async function listen(server, port) {
598
- await new Promise((resolve2, reject) => {
598
+ await new Promise((resolve3, reject) => {
599
599
  const onError = (error51) => {
600
600
  server.off("listening", onListening);
601
601
  reject(error51);
602
602
  };
603
603
  const onListening = () => {
604
604
  server.off("error", onError);
605
- resolve2();
605
+ resolve3();
606
606
  };
607
607
  server.once("error", onError);
608
608
  server.once("listening", onListening);
@@ -873,7 +873,7 @@ function persistLogin(config2, input) {
873
873
  }
874
874
  }
875
875
  async function sleep(ms) {
876
- await new Promise((resolve2) => setTimeout(resolve2, ms));
876
+ await new Promise((resolve3) => setTimeout(resolve3, ms));
877
877
  }
878
878
  var init_login = __esm({
879
879
  "src/commands/auth/login.ts"() {
@@ -18053,7 +18053,7 @@ function isChatMessageEvent(trigger) {
18053
18053
  function hasFilterValue(trigger, path2, expected) {
18054
18054
  return trigger.where?.[path2] === expected;
18055
18055
  }
18056
- var RESOURCE_KIND_SESSION, SESSION_HARNESSES, TriggerFilterScalarSchema, TriggerFilterPathSchema, TriggerFilterClauseSchema, TriggerFilterSchema, SessionTriggerCheckTimeoutSchema, TriggerEventSchema, TriggerEventsSchema, SessionTriggerSharedFields, SessionTriggerEventSourceFields, SessionTriggerBaseSchema, SessionTriggerDefinitionBaseSchema, SessionTriggerSchema, SessionApplyTriggerSchema, SessionHeartbeatTriggerBaseSchema, SessionHeartbeatTriggerSchema, SessionApplyHeartbeatTriggerSchema, SessionTriggerDefinitionSchema, SessionApplyTriggerDefinitionSchema, SessionTriggersSchema, SessionApplyTriggersSchema, AVATAR_ASSET_EXTENSIONS, MAX_AVATAR_ASSET_BYTES, MIN_AVATAR_ASSET_DIMENSION_PX, MAX_AVATAR_ASSET_DIMENSION_PX, PNG_SIGNATURE, SESSION_IDENTITY_DESCRIPTION_MAX_LENGTH, SHA256_HEX_PATTERN, SessionIdentitySchema, SessionSpecFieldsSchema, SessionSpecSchema, SessionApplySpecSchema, SessionStatusSchema, SessionResourceSchema, SessionApplyRequestSchema, SessionApplyTriggerReceiptSchema, SessionApplyResponseSchema, SESSION_TELEGRAM_IDENTITY_STATUSES, SessionTelegramIdentityStatusSchema, SessionPresenceIdentitySchema, SessionPresenceResponseSchema, SessionPresenceConnectRequestSchema, SessionPresenceConnectPendingSchema, SessionPresenceConnectResponseSchema, SessionPresenceIconRequestSchema, SessionPresenceIconResponseSchema, SessionPresenceCompleteResponseSchema;
18056
+ var RESOURCE_KIND_AGENT, LEGACY_RESOURCE_KIND_SESSION, RESOURCE_KIND_SESSION, SESSION_HARNESSES, TriggerFilterScalarSchema, TriggerFilterPathSchema, TriggerFilterClauseSchema, TriggerFilterSchema, SessionTriggerCheckTimeoutSchema, TriggerEventSchema, TriggerEventsSchema, SessionTriggerSharedFields, SessionTriggerEventSourceFields, SessionTriggerBaseSchema, SessionTriggerDefinitionBaseSchema, SessionTriggerSchema, SessionApplyTriggerSchema, SessionHeartbeatTriggerBaseSchema, SessionHeartbeatTriggerSchema, SessionApplyHeartbeatTriggerSchema, SessionTriggerDefinitionSchema, SessionApplyTriggerDefinitionSchema, SessionTriggersSchema, SessionApplyTriggersSchema, AVATAR_ASSET_EXTENSIONS, MAX_AVATAR_ASSET_BYTES, MIN_AVATAR_ASSET_DIMENSION_PX, MAX_AVATAR_ASSET_DIMENSION_PX, PNG_SIGNATURE, SESSION_IDENTITY_DESCRIPTION_MAX_LENGTH, SHA256_HEX_PATTERN, SessionIdentitySchema, SessionSpecFieldsSchema, SessionSpecSchema, SessionApplySpecSchema, SessionStatusSchema, SessionResourceSchema, SessionApplyRequestSchema, SessionApplyTriggerReceiptSchema, SessionApplyResponseSchema, SESSION_TELEGRAM_IDENTITY_STATUSES, SessionTelegramIdentityStatusSchema, SessionPresenceIdentitySchema, SessionPresenceResponseSchema, SessionPresenceConnectRequestSchema, SessionPresenceConnectPendingSchema, SessionPresenceConnectResponseSchema, SessionPresenceIconRequestSchema, SessionPresenceIconResponseSchema, SessionPresenceCompleteResponseSchema;
18057
18057
  var init_sessions = __esm({
18058
18058
  "../../packages/schemas/src/sessions.ts"() {
18059
18059
  "use strict";
@@ -18065,7 +18065,9 @@ var init_sessions = __esm({
18065
18065
  init_secrets();
18066
18066
  init_tools();
18067
18067
  init_trigger_router();
18068
- RESOURCE_KIND_SESSION = "session";
18068
+ RESOURCE_KIND_AGENT = "agent";
18069
+ LEGACY_RESOURCE_KIND_SESSION = "session";
18070
+ RESOURCE_KIND_SESSION = RESOURCE_KIND_AGENT;
18069
18071
  SESSION_HARNESSES = ["claude-code"];
18070
18072
  TriggerFilterScalarSchema = external_exports.union([
18071
18073
  external_exports.null(),
@@ -18339,7 +18341,7 @@ var init_sessions = __esm({
18339
18341
  /**
18340
18342
  * Scopes the current agent-app scope set requests that this realized
18341
18343
  * identity's install never granted. Computed at read time; non-empty means
18342
- * the install predates a scope addition and `auto sessions connect
18344
+ * the install predates a scope addition and `auto agents connect
18343
18345
  * <session> --reconnect` refreshes it.
18344
18346
  */
18345
18347
  missingScopes: external_exports.array(external_exports.string().trim().min(1)).optional()
@@ -18584,7 +18586,16 @@ var init_project_service_accounts = __esm({
18584
18586
  });
18585
18587
 
18586
18588
  // ../../packages/schemas/src/project-resources.ts
18587
- var EnvironmentApplyDocumentSchema, IdentityApplyDocumentSchema, SessionApplyDocumentSchema, ProjectApplyResourceSchema, PROJECT_RESOURCE_APPLY_ORDER, PROJECT_RESOURCE_KINDS, ProjectDeleteResourceSchema, AVATAR_ASSET_CONTENT_TYPES, MAX_AVATAR_ASSET_BASE64_LENGTH, ProjectApplyAssetSchema, ProjectApplyAssetsSchema, AvatarAssetUploadRequestSchema, AvatarAssetUploadResponseSchema, ProjectApplyRequestSchema, ProjectApplySystemConfigSchema, ProjectAppliedResourceSchema, ProjectApplyDiagnosticSchema, ProjectApplyResponseSchema;
18589
+ function normalizeLegacySessionKind(value) {
18590
+ if (isRecord(value) && value.kind === LEGACY_RESOURCE_KIND_SESSION) {
18591
+ return { ...value, kind: RESOURCE_KIND_SESSION };
18592
+ }
18593
+ return value;
18594
+ }
18595
+ function isRecord(value) {
18596
+ return typeof value === "object" && value !== null && !Array.isArray(value);
18597
+ }
18598
+ var EnvironmentApplyDocumentSchema, IdentityApplyDocumentSchema, SessionApplyDocumentSchema, ProjectApplyResourceSchema, PROJECT_RESOURCE_APPLY_ORDER, PROJECT_RESOURCE_KINDS, ProjectDeleteResourceBaseSchema, ProjectDeleteResourceSchema, AVATAR_ASSET_CONTENT_TYPES, MAX_AVATAR_ASSET_BASE64_LENGTH, ProjectApplyAssetSchema, ProjectApplyAssetsSchema, AvatarAssetUploadRequestSchema, AvatarAssetUploadResponseSchema, ProjectApplyRequestSchema, ProjectApplySystemConfigSchema, ProjectAppliedResourceSchema, ProjectApplyDiagnosticSchema, ProjectApplyResponseResourceKindSchema, ProjectApplyResponseSchema;
18588
18599
  var init_project_resources = __esm({
18589
18600
  "../../packages/schemas/src/project-resources.ts"() {
18590
18601
  "use strict";
@@ -18605,21 +18616,29 @@ var init_project_resources = __esm({
18605
18616
  RESOURCE_KIND_SESSION,
18606
18617
  SessionApplyRequestSchema.shape.spec
18607
18618
  );
18608
- ProjectApplyResourceSchema = external_exports.discriminatedUnion("kind", [
18609
- EnvironmentApplyDocumentSchema,
18610
- IdentityApplyDocumentSchema,
18611
- SessionApplyDocumentSchema
18612
- ]);
18619
+ ProjectApplyResourceSchema = external_exports.preprocess(
18620
+ normalizeLegacySessionKind,
18621
+ external_exports.discriminatedUnion("kind", [
18622
+ EnvironmentApplyDocumentSchema,
18623
+ IdentityApplyDocumentSchema,
18624
+ SessionApplyDocumentSchema
18625
+ ])
18626
+ );
18613
18627
  PROJECT_RESOURCE_APPLY_ORDER = [
18614
18628
  RESOURCE_KIND_ENVIRONMENT,
18615
18629
  RESOURCE_KIND_IDENTITY,
18616
18630
  RESOURCE_KIND_SESSION
18617
18631
  ];
18618
18632
  PROJECT_RESOURCE_KINDS = PROJECT_RESOURCE_APPLY_ORDER;
18619
- ProjectDeleteResourceSchema = external_exports.object({
18620
- kind: external_exports.enum(PROJECT_RESOURCE_KINDS),
18633
+ ProjectDeleteResourceBaseSchema = external_exports.object({
18621
18634
  name: external_exports.string().trim().min(1)
18622
18635
  });
18636
+ ProjectDeleteResourceSchema = external_exports.preprocess(
18637
+ normalizeLegacySessionKind,
18638
+ ProjectDeleteResourceBaseSchema.extend({
18639
+ kind: external_exports.enum(PROJECT_RESOURCE_KINDS)
18640
+ })
18641
+ );
18623
18642
  AVATAR_ASSET_CONTENT_TYPES = ["image/png", "image/jpeg"];
18624
18643
  MAX_AVATAR_ASSET_BASE64_LENGTH = Math.ceil(MAX_AVATAR_ASSET_BYTES / 3) * 4 + 4;
18625
18644
  ProjectApplyAssetSchema = external_exports.object({
@@ -18673,6 +18692,12 @@ var init_project_resources = __esm({
18673
18692
  ]),
18674
18693
  name: external_exports.string().min(1)
18675
18694
  });
18695
+ ProjectApplyResponseResourceKindSchema = external_exports.union([
18696
+ external_exports.enum(PROJECT_RESOURCE_KINDS),
18697
+ external_exports.literal(LEGACY_RESOURCE_KIND_SESSION)
18698
+ ]).transform(
18699
+ (kind) => kind === LEGACY_RESOURCE_KIND_SESSION ? RESOURCE_KIND_SESSION : kind
18700
+ );
18676
18701
  ProjectApplyResponseSchema = external_exports.object({
18677
18702
  dryRun: external_exports.boolean().default(false),
18678
18703
  resources: external_exports.array(ProjectAppliedResourceSchema),
@@ -18681,14 +18706,14 @@ var init_project_resources = __esm({
18681
18706
  plan: external_exports.array(
18682
18707
  external_exports.object({
18683
18708
  action: external_exports.enum(["create", "update", "unchanged", "archive"]),
18684
- kind: external_exports.enum(PROJECT_RESOURCE_KINDS),
18709
+ kind: ProjectApplyResponseResourceKindSchema,
18685
18710
  name: external_exports.string().min(1),
18686
18711
  uid: external_exports.string().min(1).optional()
18687
18712
  })
18688
18713
  ).default([]),
18689
18714
  pruned: external_exports.array(
18690
18715
  external_exports.object({
18691
- kind: external_exports.enum(PROJECT_RESOURCE_KINDS),
18716
+ kind: ProjectApplyResponseResourceKindSchema,
18692
18717
  name: external_exports.string().min(1),
18693
18718
  uid: external_exports.string().min(1)
18694
18719
  })
@@ -21183,7 +21208,7 @@ var init_package = __esm({
21183
21208
  "package.json"() {
21184
21209
  package_default = {
21185
21210
  name: "@autohq/cli",
21186
- version: "0.1.133",
21211
+ version: "0.1.135",
21187
21212
  license: "SEE LICENSE IN README.md",
21188
21213
  publishConfig: {
21189
21214
  access: "public"
@@ -21317,7 +21342,7 @@ function stampAvatarSha256(resource, assets) {
21317
21342
  }
21318
21343
  };
21319
21344
  }
21320
- if (resource.kind === "session" && typeof resource.spec.identity === "object" && resource.spec.identity !== null && !Array.isArray(resource.spec.identity)) {
21345
+ if (resource.kind === RESOURCE_KIND_SESSION && typeof resource.spec.identity === "object" && resource.spec.identity !== null && !Array.isArray(resource.spec.identity)) {
21321
21346
  const identity2 = resource.spec.identity;
21322
21347
  const avatar = identity2.avatar;
21323
21348
  const asset = avatar ? assets[avatar.asset] : void 0;
@@ -21344,19 +21369,14 @@ var MAX_APPLY_REQUEST_BODY_BYTES;
21344
21369
  var init_assets = __esm({
21345
21370
  "src/commands/apply/assets.ts"() {
21346
21371
  "use strict";
21372
+ init_src();
21347
21373
  init_resources2();
21348
21374
  MAX_APPLY_REQUEST_BODY_BYTES = 4 * 1024 * 1024;
21349
21375
  }
21350
21376
  });
21351
21377
 
21352
- // src/commands/apply/files.ts
21353
- import { createHash as createHash3 } from "crypto";
21354
- import {
21355
- readFileSync as readFileSync3,
21356
- readdirSync as readdirSync2,
21357
- realpathSync,
21358
- statSync
21359
- } from "fs";
21378
+ // src/commands/agents/authoring.ts
21379
+ import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync } from "fs";
21360
21380
  import {
21361
21381
  basename as basename2,
21362
21382
  dirname as dirname4,
@@ -21365,7 +21385,436 @@ import {
21365
21385
  join as join3,
21366
21386
  resolve
21367
21387
  } from "path";
21368
- import { parseAllDocuments as parseYamlDocuments } from "yaml";
21388
+ import { parseAllDocuments as parseYamlDocuments, stringify } from "yaml";
21389
+ function compileAgentFile(path2) {
21390
+ const context = { graph: /* @__PURE__ */ new Map(), removals: [] };
21391
+ const document = readSingleDocument(path2);
21392
+ const compiled = normalizeAgentDocument(
21393
+ compileAgentDocument(document, path2, [], context)
21394
+ );
21395
+ const parsed = ProjectApplyResourceSchema.safeParse(compiled);
21396
+ if (!parsed.success) {
21397
+ throw new Error(`Invalid compiled agent ${path2}: ${parsed.error.message}`);
21398
+ }
21399
+ if (parsed.data.kind !== RESOURCE_KIND_SESSION) {
21400
+ throw new Error(
21401
+ `Invalid compiled agent ${path2}: expected kind "agent", got "${parsed.data.kind}"`
21402
+ );
21403
+ }
21404
+ return {
21405
+ resource: parsed.data,
21406
+ graph: [...context.graph.values()],
21407
+ removals: context.removals
21408
+ };
21409
+ }
21410
+ function compileAgentDocumentValue(document, path2) {
21411
+ const context = { graph: /* @__PURE__ */ new Map(), removals: [] };
21412
+ const compiled = normalizeAgentDocument(
21413
+ compileAgentDocument(document, path2, [], context)
21414
+ );
21415
+ const parsed = ProjectApplyResourceSchema.safeParse(compiled);
21416
+ if (!parsed.success) {
21417
+ throw new Error(`Invalid compiled agent ${path2}: ${parsed.error.message}`);
21418
+ }
21419
+ if (parsed.data.kind !== RESOURCE_KIND_SESSION) {
21420
+ throw new Error(
21421
+ `Invalid compiled agent ${path2}: expected kind "agent", got "${parsed.data.kind}"`
21422
+ );
21423
+ }
21424
+ return {
21425
+ resource: parsed.data,
21426
+ graph: [...context.graph.values()],
21427
+ removals: context.removals
21428
+ };
21429
+ }
21430
+ function renderCompiledAgentYaml(result) {
21431
+ return stringify(result.resource).trimEnd();
21432
+ }
21433
+ function renderAgentExplain(result) {
21434
+ const lines = [
21435
+ `agent ${result.resource.metadata.name}`,
21436
+ `files ${result.graph.length}`
21437
+ ];
21438
+ for (const node of result.graph) {
21439
+ lines.push(node.path);
21440
+ for (const imported of node.imports) {
21441
+ lines.push(` imports ${imported}`);
21442
+ }
21443
+ }
21444
+ if (result.removals.length > 0) {
21445
+ lines.push("removals");
21446
+ for (const removal of result.removals) {
21447
+ lines.push(
21448
+ ` ${removal.target}: ${removal.names.join(", ")} (${removal.path})`
21449
+ );
21450
+ }
21451
+ }
21452
+ return lines.join("\n");
21453
+ }
21454
+ function readLocalAgentAuthoringStatuses(input) {
21455
+ const agentsDirectory = join3(
21456
+ input?.directory ?? join3(process.cwd(), ".auto"),
21457
+ "agents"
21458
+ );
21459
+ let entries;
21460
+ try {
21461
+ entries = readdirSync2(agentsDirectory).filter(
21462
+ (entry) => AGENT_FILE_EXTENSIONS.includes(
21463
+ extname(
21464
+ entry
21465
+ ).toLowerCase()
21466
+ )
21467
+ ).sort((left, right) => left.localeCompare(right));
21468
+ } catch {
21469
+ return [];
21470
+ }
21471
+ return entries.map((entry) => {
21472
+ const path2 = join3(agentsDirectory, entry);
21473
+ const fallbackName = basename2(entry, extname(entry));
21474
+ try {
21475
+ const result = compileAgentFile(path2);
21476
+ return {
21477
+ name: result.resource.metadata.name,
21478
+ path: path2,
21479
+ ok: true,
21480
+ imports: importedEdgeCount(result.graph),
21481
+ removals: result.removals.reduce(
21482
+ (count, removal) => count + removal.names.length,
21483
+ 0
21484
+ )
21485
+ };
21486
+ } catch (error51) {
21487
+ return {
21488
+ name: fallbackName,
21489
+ path: path2,
21490
+ ok: false,
21491
+ imports: 0,
21492
+ removals: 0,
21493
+ error: error51 instanceof Error ? error51.message : String(error51)
21494
+ };
21495
+ }
21496
+ });
21497
+ }
21498
+ function importedAgentAuthoringPaths(paths) {
21499
+ const imported = /* @__PURE__ */ new Set();
21500
+ for (const path2 of paths) {
21501
+ for (const document of readDocuments(path2)) {
21502
+ discoverImports(document, path2, [], imported);
21503
+ }
21504
+ }
21505
+ return imported;
21506
+ }
21507
+ function resolveAgentAuthoringPath(input) {
21508
+ const candidate = resolve(input.agent);
21509
+ if (isAgentFile(candidate)) {
21510
+ return candidate;
21511
+ }
21512
+ const agentsDirectory = join3(
21513
+ input.directory ?? join3(process.cwd(), ".auto"),
21514
+ "agents"
21515
+ );
21516
+ for (const extension of AGENT_FILE_EXTENSIONS) {
21517
+ const path2 = join3(agentsDirectory, `${input.agent}${extension}`);
21518
+ if (isAgentFile(path2)) {
21519
+ return path2;
21520
+ }
21521
+ }
21522
+ throw new Error(
21523
+ `Agent authoring file not found for "${input.agent}" under ${agentsDirectory}`
21524
+ );
21525
+ }
21526
+ function discoverImports(document, path2, stack, imported) {
21527
+ const resolvedPath = resolve(path2);
21528
+ if (stack.includes(resolvedPath)) {
21529
+ throw new Error(
21530
+ `Agent import cycle detected: ${[...stack, resolvedPath].join(" -> ")}`
21531
+ );
21532
+ }
21533
+ if (!isRecord2(document)) {
21534
+ return;
21535
+ }
21536
+ for (const importPath of importPaths(document)) {
21537
+ const resolvedImport = resolveImportPath(importPath, resolvedPath);
21538
+ imported.add(resolvedImport);
21539
+ discoverImports(
21540
+ readSingleDocument(resolvedImport),
21541
+ resolvedImport,
21542
+ [...stack, resolvedPath],
21543
+ imported
21544
+ );
21545
+ }
21546
+ }
21547
+ function compileAgentDocument(document, path2, stack, context) {
21548
+ const resolvedPath = resolve(path2);
21549
+ if (stack.includes(resolvedPath)) {
21550
+ throw new Error(
21551
+ `Agent import cycle detected: ${[...stack, resolvedPath].join(" -> ")}`
21552
+ );
21553
+ }
21554
+ if (!isRecord2(document)) {
21555
+ throw new Error(`Invalid agent authoring file ${path2}: expected object`);
21556
+ }
21557
+ const imports = importPaths(document).map(
21558
+ (importPath) => resolveImportPath(importPath, resolvedPath)
21559
+ );
21560
+ context.graph.set(resolvedPath, { path: resolvedPath, imports });
21561
+ let merged = {};
21562
+ for (const imported of imports) {
21563
+ const importedDocument = readSingleDocument(imported);
21564
+ merged = mergeValues2(
21565
+ merged,
21566
+ compileAgentDocument(
21567
+ importedDocument,
21568
+ imported,
21569
+ [...stack, resolvedPath],
21570
+ context
21571
+ ),
21572
+ []
21573
+ );
21574
+ }
21575
+ const removals = removalDirectives(document, resolvedPath);
21576
+ for (const removal of removals) {
21577
+ merged = applyRemoval(merged, removal);
21578
+ context.removals.push(removal);
21579
+ }
21580
+ return mergeValues2(merged, sanitizedAgentDocument(document), []);
21581
+ }
21582
+ function readSingleDocument(path2) {
21583
+ const documents = readDocuments(path2);
21584
+ if (documents.length !== 1) {
21585
+ throw new Error(
21586
+ `Agent authoring file ${path2} must contain exactly one document`
21587
+ );
21588
+ }
21589
+ return documents[0];
21590
+ }
21591
+ function readDocuments(path2) {
21592
+ const source = readFileSync3(path2, "utf8");
21593
+ return parseYamlDocuments(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
21594
+ }
21595
+ function importPaths(document) {
21596
+ const value = document.imports ?? document.import;
21597
+ if (value === void 0) {
21598
+ return [];
21599
+ }
21600
+ const values = Array.isArray(value) ? value : [value];
21601
+ return values.map((item) => {
21602
+ if (typeof item !== "string" || item.trim().length === 0) {
21603
+ throw new Error("Agent imports must be non-empty string paths");
21604
+ }
21605
+ return item.trim();
21606
+ });
21607
+ }
21608
+ function resolveImportPath(importPath, importerPath) {
21609
+ if (isAbsolute(importPath) || /^[A-Za-z]+:\/\//.test(importPath)) {
21610
+ throw new Error(`Agent import must be a relative path: ${importPath}`);
21611
+ }
21612
+ const resolved = resolve(dirname4(importerPath), importPath);
21613
+ if (!isAgentFile(resolved)) {
21614
+ throw new Error(
21615
+ `Agent import not found: ${importPath} from ${importerPath}`
21616
+ );
21617
+ }
21618
+ return resolved;
21619
+ }
21620
+ function sanitizedAgentDocument(document) {
21621
+ const next = { ...document };
21622
+ delete next.import;
21623
+ delete next.imports;
21624
+ delete next.remove;
21625
+ if (isRecord2(next.spec) && "remove" in next.spec) {
21626
+ const spec = { ...next.spec };
21627
+ delete spec.remove;
21628
+ next.spec = spec;
21629
+ }
21630
+ return next;
21631
+ }
21632
+ function normalizeAgentDocument(document) {
21633
+ if (!isRecord2(document) || "kind" in document) {
21634
+ return document;
21635
+ }
21636
+ return { ...document, kind: RESOURCE_KIND_SESSION };
21637
+ }
21638
+ function removalDirectives(document, path2) {
21639
+ const raw = mergeValues2(
21640
+ isRecord2(document.remove) ? document.remove : {},
21641
+ isRecord2(document.spec) && isRecord2(document.spec.remove) ? document.spec.remove : {},
21642
+ []
21643
+ );
21644
+ if (!isRecord2(raw)) {
21645
+ return [];
21646
+ }
21647
+ const directives = [];
21648
+ for (const [target, value] of Object.entries(raw)) {
21649
+ const names = stringList(value, `remove.${target}`);
21650
+ if (names.length > 0) {
21651
+ directives.push({ path: path2, target, names });
21652
+ }
21653
+ }
21654
+ return directives;
21655
+ }
21656
+ function stringList(value, label) {
21657
+ const values = Array.isArray(value) ? value : [value];
21658
+ return values.map((item) => {
21659
+ if (typeof item !== "string" || item.trim().length === 0) {
21660
+ throw new Error(`${label} entries must be non-empty strings`);
21661
+ }
21662
+ return item.trim();
21663
+ });
21664
+ }
21665
+ function applyRemoval(value, removal) {
21666
+ if (!isRecord2(value)) {
21667
+ return value;
21668
+ }
21669
+ const next = structuredClone(value);
21670
+ if (!isRecord2(next.spec)) {
21671
+ return next;
21672
+ }
21673
+ const spec = { ...next.spec };
21674
+ next.spec = spec;
21675
+ switch (removal.target) {
21676
+ case "tools":
21677
+ case "env": {
21678
+ if (!isRecord2(spec[removal.target])) {
21679
+ return next;
21680
+ }
21681
+ const collection = {
21682
+ ...spec[removal.target]
21683
+ };
21684
+ for (const name of removal.names) {
21685
+ delete collection[name];
21686
+ }
21687
+ spec[removal.target] = collection;
21688
+ return next;
21689
+ }
21690
+ case "mounts":
21691
+ case "triggers": {
21692
+ const value2 = spec[removal.target];
21693
+ if (!Array.isArray(value2)) {
21694
+ return next;
21695
+ }
21696
+ const collection = value2;
21697
+ spec[removal.target] = collection.filter(
21698
+ (item) => !removal.names.includes(
21699
+ collectionItemKey(removal.target, item) ?? ""
21700
+ )
21701
+ );
21702
+ return next;
21703
+ }
21704
+ default:
21705
+ throw new Error(
21706
+ `Unsupported agent remove target "${removal.target}"; supported targets are tools, env, mounts, triggers`
21707
+ );
21708
+ }
21709
+ }
21710
+ function mergeValues2(base, override, path2) {
21711
+ if (override === void 0) {
21712
+ return base;
21713
+ }
21714
+ if (Array.isArray(base) && Array.isArray(override)) {
21715
+ return mergeArrays(base, override, path2);
21716
+ }
21717
+ if (isRecord2(base) && isRecord2(override)) {
21718
+ const merged = { ...base };
21719
+ for (const [key, value] of Object.entries(override)) {
21720
+ merged[key] = mergeValues2(merged[key], value, [...path2, key]);
21721
+ }
21722
+ return merged;
21723
+ }
21724
+ return override;
21725
+ }
21726
+ function mergeArrays(base, override, path2) {
21727
+ const pathKey = path2.join(".");
21728
+ if (pathKey !== "spec.mounts" && pathKey !== "spec.triggers") {
21729
+ return [...base, ...override];
21730
+ }
21731
+ const merged = [...base];
21732
+ const indexByKey = /* @__PURE__ */ new Map();
21733
+ for (const [index, item] of merged.entries()) {
21734
+ const key = collectionItemKey(path2.at(-1) ?? "", item);
21735
+ if (key) {
21736
+ indexByKey.set(key, index);
21737
+ }
21738
+ }
21739
+ for (const item of override) {
21740
+ const key = collectionItemKey(path2.at(-1) ?? "", item);
21741
+ if (key && indexByKey.has(key)) {
21742
+ merged[indexByKey.get(key) ?? 0] = mergeValues2(
21743
+ merged[indexByKey.get(key) ?? 0],
21744
+ item,
21745
+ path2
21746
+ );
21747
+ continue;
21748
+ }
21749
+ if (key) {
21750
+ indexByKey.set(key, merged.length);
21751
+ }
21752
+ merged.push(item);
21753
+ }
21754
+ return merged;
21755
+ }
21756
+ function collectionItemKey(collection, item) {
21757
+ if (!isRecord2(item)) {
21758
+ return void 0;
21759
+ }
21760
+ if (typeof item.name === "string") {
21761
+ return item.name;
21762
+ }
21763
+ if (collection === "mounts" && typeof item.mountPath === "string") {
21764
+ return item.mountPath;
21765
+ }
21766
+ if (collection === "triggers") {
21767
+ if (typeof item.event === "string") {
21768
+ return item.event;
21769
+ }
21770
+ if (Array.isArray(item.events)) {
21771
+ return item.events.filter((event) => typeof event === "string").join(",");
21772
+ }
21773
+ if (typeof item.cron === "string") {
21774
+ return `cron:${item.cron}:${typeof item.timezone === "string" ? item.timezone : ""}`;
21775
+ }
21776
+ }
21777
+ return void 0;
21778
+ }
21779
+ function importedEdgeCount(graph) {
21780
+ return graph.reduce((count, node) => count + node.imports.length, 0);
21781
+ }
21782
+ function isAgentFile(path2) {
21783
+ try {
21784
+ return statSync(path2).isFile();
21785
+ } catch {
21786
+ return false;
21787
+ }
21788
+ }
21789
+ function isRecord2(value) {
21790
+ return typeof value === "object" && value !== null && !Array.isArray(value);
21791
+ }
21792
+ var AGENT_FILE_EXTENSIONS;
21793
+ var init_authoring = __esm({
21794
+ "src/commands/agents/authoring.ts"() {
21795
+ "use strict";
21796
+ init_src();
21797
+ AGENT_FILE_EXTENSIONS = [".yaml", ".yml", ".json"];
21798
+ }
21799
+ });
21800
+
21801
+ // src/commands/apply/files.ts
21802
+ import { createHash as createHash3 } from "crypto";
21803
+ import {
21804
+ readFileSync as readFileSync4,
21805
+ readdirSync as readdirSync3,
21806
+ realpathSync,
21807
+ statSync as statSync2
21808
+ } from "fs";
21809
+ import {
21810
+ basename as basename3,
21811
+ dirname as dirname5,
21812
+ extname as extname2,
21813
+ isAbsolute as isAbsolute2,
21814
+ join as join4,
21815
+ resolve as resolve2
21816
+ } from "path";
21817
+ import { parseAllDocuments as parseYamlDocuments2 } from "yaml";
21369
21818
  function readProjectApplyRequest(options) {
21370
21819
  if (options.file && options.directory) {
21371
21820
  throw new Error("Cannot use --file with --directory.");
@@ -21378,7 +21827,7 @@ function readProjectApplyRequest(options) {
21378
21827
  );
21379
21828
  return { ...request, assets: assets2 };
21380
21829
  }
21381
- const directory = options.directory ?? join3(process.cwd(), ".auto");
21830
+ const directory = options.directory ?? join4(process.cwd(), ".auto");
21382
21831
  const files = applyFiles(directory);
21383
21832
  if (files.length === 0) {
21384
21833
  throw new Error(`No resource files found in ${directory}`);
@@ -21389,7 +21838,7 @@ function readProjectApplyRequest(options) {
21389
21838
  for (const resource of request.resources) {
21390
21839
  if (resource.kind !== kind) {
21391
21840
  throw new Error(
21392
- `Resource kind "${resource.kind}" in ${path2} does not match .auto/${APPLY_DIRECTORIES[kind]}`
21841
+ `Resource kind "${resource.kind}" in ${path2} does not match .auto/${primaryApplyDirectory(kind)}`
21393
21842
  );
21394
21843
  }
21395
21844
  resources.push(resource);
@@ -21430,23 +21879,30 @@ function mcpOAuthSessionToolConnectionsFromAppliedResources(resources) {
21430
21879
  function applyFiles(root) {
21431
21880
  const files = [];
21432
21881
  for (const kind of PROJECT_RESOURCE_APPLY_ORDER) {
21433
- const directory = APPLY_DIRECTORIES[kind];
21434
- const path2 = join3(root, directory);
21435
- let entries;
21436
- try {
21437
- entries = readdirSync2(path2, { withFileTypes: true });
21438
- } catch {
21439
- continue;
21882
+ const kindFiles = [];
21883
+ for (const directory of applyDirectories(kind)) {
21884
+ const path2 = join4(root, directory);
21885
+ let entries;
21886
+ try {
21887
+ entries = readdirSync3(path2, { withFileTypes: true });
21888
+ } catch {
21889
+ continue;
21890
+ }
21891
+ kindFiles.push(...resourceApplyFiles(path2, entries));
21440
21892
  }
21441
- files.push(
21442
- ...resourceApplyFiles(path2, entries).map((file2) => ({
21443
- kind,
21444
- path: file2
21445
- }))
21446
- );
21893
+ const importedFiles = kind === RESOURCE_KIND_SESSION ? importedAgentAuthoringPaths(kindFiles) : /* @__PURE__ */ new Set();
21894
+ const appliedFiles = kind === RESOURCE_KIND_SESSION ? kindFiles.filter(
21895
+ (path2) => !importedFiles.has(resolve2(path2)) && !isSharedAgentAuthoringFile(root, path2)
21896
+ ) : kindFiles;
21897
+ files.push(...appliedFiles.map((path2) => ({ kind, path: path2 })));
21447
21898
  }
21448
21899
  return files;
21449
21900
  }
21901
+ function isSharedAgentAuthoringFile(root, path2) {
21902
+ const agentsSharedRoot = resolve2(root, "agents", "shared");
21903
+ const resolvedPath = resolve2(path2);
21904
+ return resolvedPath.startsWith(`${agentsSharedRoot}/`);
21905
+ }
21450
21906
  function mcpOAuthSessionToolConnectionsFromSessionTools(input) {
21451
21907
  return Object.entries(input.tools).flatMap(([alias, tool]) => {
21452
21908
  if (tool.kind !== "mcp_remote" || tool.disabled || tool.auth.kind !== "mcp_oauth") {
@@ -21462,10 +21918,10 @@ function mcpOAuthSessionToolConnectionsFromSessionTools(input) {
21462
21918
  });
21463
21919
  }
21464
21920
  function readApplyDocumentFile(path2) {
21465
- const source = readFileSync3(path2, "utf8");
21921
+ const source = readFileSync4(path2, "utf8");
21466
21922
  let documents;
21467
21923
  try {
21468
- documents = parseYamlDocuments(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
21924
+ documents = parseYamlDocuments2(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
21469
21925
  } catch (error51) {
21470
21926
  throw new Error(
21471
21927
  `Invalid apply file: ${error51 instanceof Error ? error51.message : String(error51)}`
@@ -21484,12 +21940,15 @@ function readApplyDocumentFile(path2) {
21484
21940
  delete: [],
21485
21941
  dryRun: false,
21486
21942
  prune: false,
21487
- resources: documents.map(readApplyDocument),
21943
+ resources: documents.map((document) => readApplyDocument(document, path2)),
21488
21944
  assets: {}
21489
21945
  };
21490
21946
  }
21491
- function readApplyDocument(document) {
21947
+ function readApplyDocument(document, path2) {
21492
21948
  const candidate = applyCandidate(document);
21949
+ if (candidate.kind === RESOURCE_KIND_SESSION) {
21950
+ return compileAgentDocumentValue(document, path2).resource;
21951
+ }
21493
21952
  const parsed = APPLY_SCHEMAS[candidate.kind].safeParse(candidate.value);
21494
21953
  if (!parsed.success) {
21495
21954
  throw new Error(
@@ -21514,7 +21973,7 @@ function readApplyAssets(resources, projectRoot) {
21514
21973
  resourceName: target.resourceName,
21515
21974
  projectRoot
21516
21975
  });
21517
- const bytes = readFileSync3(resolvedPath);
21976
+ const bytes = readFileSync4(resolvedPath);
21518
21977
  const dimensionsProblem = avatarAssetDimensionsProblem(bytes);
21519
21978
  if (dimensionsProblem) {
21520
21979
  throw new Error(
@@ -21523,7 +21982,7 @@ function readApplyAssets(resources, projectRoot) {
21523
21982
  }
21524
21983
  assets[target.asset] = {
21525
21984
  sha256: createHash3("sha256").update(bytes).digest("hex"),
21526
- contentType: extname(resolvedPath).toLowerCase() === ".png" ? "image/png" : "image/jpeg",
21985
+ contentType: extname2(resolvedPath).toLowerCase() === ".png" ? "image/png" : "image/jpeg",
21527
21986
  dataBase64: bytes.toString("base64")
21528
21987
  };
21529
21988
  }
@@ -21541,7 +22000,7 @@ function avatarAssetTarget(resource) {
21541
22000
  return void 0;
21542
22001
  }
21543
22002
  function validateSessionAvatarAsset(input) {
21544
- if (isAbsolute(input.asset)) {
22003
+ if (isAbsolute2(input.asset)) {
21545
22004
  throw new Error(
21546
22005
  `Invalid identity avatar asset for "${input.resourceName}": asset path must be relative`
21547
22006
  );
@@ -21553,10 +22012,10 @@ function validateSessionAvatarAsset(input) {
21553
22012
  );
21554
22013
  }
21555
22014
  const projectRoot = realpathSync(input.projectRoot);
21556
- const assetPath = resolve(projectRoot, input.asset);
22015
+ const assetPath = resolve2(projectRoot, input.asset);
21557
22016
  let assetsRoot;
21558
22017
  try {
21559
- assetsRoot = realpathSync(resolve(projectRoot, ".auto", "assets"));
22018
+ assetsRoot = realpathSync(resolve2(projectRoot, ".auto", "assets"));
21560
22019
  } catch {
21561
22020
  throw new Error(
21562
22021
  `Invalid identity avatar asset for "${input.resourceName}": .auto/assets directory does not exist`
@@ -21566,7 +22025,7 @@ function validateSessionAvatarAsset(input) {
21566
22025
  let stat;
21567
22026
  try {
21568
22027
  resolvedAssetPath = realpathSync(assetPath);
21569
- stat = statSync(resolvedAssetPath);
22028
+ stat = statSync2(resolvedAssetPath);
21570
22029
  } catch {
21571
22030
  throw new Error(
21572
22031
  `Invalid identity avatar asset for "${input.resourceName}": ${input.asset} does not exist`
@@ -21587,7 +22046,7 @@ function validateSessionAvatarAsset(input) {
21587
22046
  `Invalid identity avatar asset for "${input.resourceName}": asset must be ${MAX_AVATAR_ASSET_BYTES} bytes or smaller`
21588
22047
  );
21589
22048
  }
21590
- const extension = extname(resolvedAssetPath).toLowerCase();
22049
+ const extension = extname2(resolvedAssetPath).toLowerCase();
21591
22050
  if (!ALLOWED_AVATAR_EXTENSIONS.has(extension)) {
21592
22051
  throw new Error(
21593
22052
  `Invalid identity avatar asset for "${input.resourceName}": asset must be a PNG or JPEG image`
@@ -21596,16 +22055,16 @@ function validateSessionAvatarAsset(input) {
21596
22055
  return resolvedAssetPath;
21597
22056
  }
21598
22057
  function applyProjectRoot(directory) {
21599
- const resolved = resolve(directory);
21600
- return basename2(resolved) === ".auto" ? dirname4(resolved) : resolved;
22058
+ const resolved = resolve2(directory);
22059
+ return basename3(resolved) === ".auto" ? dirname5(resolved) : resolved;
21601
22060
  }
21602
22061
  function applyFileProjectRoot(file2) {
21603
- let dir = dirname4(resolve(file2));
22062
+ let dir = dirname5(resolve2(file2));
21604
22063
  while (true) {
21605
- if (basename2(dir) === ".auto") {
21606
- return dirname4(dir);
22064
+ if (basename3(dir) === ".auto") {
22065
+ return dirname5(dir);
21607
22066
  }
21608
- const parent = dirname4(dir);
22067
+ const parent = dirname5(dir);
21609
22068
  if (parent === dir) {
21610
22069
  return process.cwd();
21611
22070
  }
@@ -21616,12 +22075,21 @@ function isInside(path2, parent) {
21616
22075
  return path2.startsWith(`${parent}/`);
21617
22076
  }
21618
22077
  function applyCandidate(document) {
21619
- if (!isRecord(document) || !("kind" in document)) {
22078
+ if (!isRecord3(document) || !("kind" in document)) {
21620
22079
  return { kind: RESOURCE_KIND_SESSION, value: document };
21621
22080
  }
22081
+ if (document.kind === LEGACY_RESOURCE_KIND_SESSION) {
22082
+ return {
22083
+ kind: RESOURCE_KIND_SESSION,
22084
+ value: {
22085
+ metadata: document.metadata,
22086
+ spec: document.spec
22087
+ }
22088
+ };
22089
+ }
21622
22090
  if (!PROJECT_RESOURCE_APPLY_ORDER.includes(document.kind)) {
21623
22091
  throw new Error(
21624
- `Unsupported apply resource kind "${String(document.kind)}"; supported kinds are ${PROJECT_RESOURCE_APPLY_ORDER.map((kind2) => `"${kind2}"`).join(", ")}`
22092
+ `Unsupported apply resource kind "${String(document.kind)}"; supported kinds are ${PROJECT_RESOURCE_APPLY_ORDER.map((kind2) => `"${kind2}"`).join(", ")} (legacy alias "session" is also accepted)`
21625
22093
  );
21626
22094
  }
21627
22095
  const kind = document.kind;
@@ -21633,16 +22101,22 @@ function applyCandidate(document) {
21633
22101
  }
21634
22102
  };
21635
22103
  }
21636
- function isRecord(value) {
22104
+ function applyDirectories(kind) {
22105
+ return [...LEGACY_APPLY_DIRECTORIES[kind] ?? [], APPLY_DIRECTORIES[kind]];
22106
+ }
22107
+ function primaryApplyDirectory(kind) {
22108
+ return APPLY_DIRECTORIES[kind];
22109
+ }
22110
+ function isRecord3(value) {
21637
22111
  return typeof value === "object" && value !== null && !Array.isArray(value);
21638
22112
  }
21639
22113
  function resourceApplyFiles(directory, entries) {
21640
22114
  const files = [];
21641
22115
  for (const entry of entries) {
21642
- const path2 = join3(directory, entry.name);
22116
+ const path2 = join4(directory, entry.name);
21643
22117
  if (entry.isDirectory()) {
21644
22118
  files.push(
21645
- ...resourceApplyFiles(path2, readdirSync2(path2, { withFileTypes: true }))
22119
+ ...resourceApplyFiles(path2, readdirSync3(path2, { withFileTypes: true }))
21646
22120
  );
21647
22121
  continue;
21648
22122
  }
@@ -21652,20 +22126,24 @@ function resourceApplyFiles(directory, entries) {
21652
22126
  }
21653
22127
  return files.sort((left, right) => left.localeCompare(right));
21654
22128
  }
21655
- var APPLY_DIRECTORIES, APPLY_SCHEMAS, ALLOWED_AVATAR_EXTENSIONS;
22129
+ var APPLY_DIRECTORIES, LEGACY_APPLY_DIRECTORIES, APPLY_SCHEMAS, ALLOWED_AVATAR_EXTENSIONS;
21656
22130
  var init_files = __esm({
21657
22131
  "src/commands/apply/files.ts"() {
21658
22132
  "use strict";
21659
22133
  init_src();
22134
+ init_authoring();
21660
22135
  APPLY_DIRECTORIES = {
21661
22136
  environment: "environments",
21662
22137
  identity: "identities",
21663
- session: "sessions"
22138
+ agent: "agents"
22139
+ };
22140
+ LEGACY_APPLY_DIRECTORIES = {
22141
+ [RESOURCE_KIND_SESSION]: ["sessions"]
21664
22142
  };
21665
22143
  APPLY_SCHEMAS = {
21666
22144
  environment: EnvironmentApplyRequestSchema,
21667
22145
  identity: IdentityApplyRequestSchema,
21668
- session: SessionApplyRequestSchema
22146
+ [RESOURCE_KIND_SESSION]: SessionApplyRequestSchema
21669
22147
  };
21670
22148
  ALLOWED_AVATAR_EXTENSIONS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png"]);
21671
22149
  }
@@ -21754,7 +22232,7 @@ async function connectSessionTool(input) {
21754
22232
  }
21755
22233
  );
21756
22234
  input.writeOutput(
21757
- `connected session/${completed.session ?? input.session} tool/${completed.tool} connection/${completed.connection}`
22235
+ `connected agent/${completed.session ?? input.session} tool/${completed.tool} connection/${completed.connection}`
21758
22236
  );
21759
22237
  } finally {
21760
22238
  callback.close();
@@ -21792,7 +22270,7 @@ function sessionToolConnectionScopeContext(input) {
21792
22270
  function writeConnectionStart(writeOutput, result) {
21793
22271
  writeOutput(result.message);
21794
22272
  writeOutput(
21795
- `connect session/${result.session} tool/${result.tool} connection/${result.connection}`
22273
+ `connect agent/${result.session} tool/${result.tool} connection/${result.connection}`
21796
22274
  );
21797
22275
  writeOutput(`authorization_url ${result.authorizationUrl}`);
21798
22276
  }
@@ -21871,7 +22349,7 @@ async function applyProjectInput(input) {
21871
22349
  )) {
21872
22350
  input.writeOutput(
21873
22351
  style.dim(
21874
- `would connect session/${item.session} tool/${item.tool} connection/${item.connection}`
22352
+ `would connect agent/${item.session} tool/${item.tool} connection/${item.connection}`
21875
22353
  )
21876
22354
  );
21877
22355
  }
@@ -21959,8 +22437,11 @@ function parseProjectResourceReference(resource) {
21959
22437
  throw new Error('Resource must be formatted as "kind/name"');
21960
22438
  }
21961
22439
  if (!PROJECT_RESOURCE_KINDS.includes(kind)) {
22440
+ if (kind === LEGACY_RESOURCE_KIND_SESSION) {
22441
+ return { kind: RESOURCE_KIND_SESSION, name };
22442
+ }
21962
22443
  throw new Error(
21963
- `Unsupported resource kind "${kind}"; supported kinds are ${PROJECT_RESOURCE_KINDS.map((value) => `"${value}"`).join(", ")}`
22444
+ `Unsupported resource kind "${kind}"; supported kinds are ${PROJECT_RESOURCE_KINDS.map((value) => `"${value}"`).join(", ")} (legacy alias "session" is also accepted)`
21964
22445
  );
21965
22446
  }
21966
22447
  return { kind, name };
@@ -22006,8 +22487,8 @@ var init_resources3 = __esm({
22006
22487
  [RESOURCE_KIND_SESSION]: {
22007
22488
  kind: RESOURCE_KIND_SESSION,
22008
22489
  section: "sessions",
22009
- label: "Session",
22010
- pluralLabel: "Sessions"
22490
+ label: "Agent",
22491
+ pluralLabel: "Agents"
22011
22492
  }
22012
22493
  };
22013
22494
  PROJECT_RESOURCE_TUI_ORDER = [
@@ -22035,10 +22516,10 @@ var init_resources3 = __esm({
22035
22516
 
22036
22517
  // src/commands/edit/actions.ts
22037
22518
  import { spawn as spawn2 } from "child_process";
22038
- import { mkdtempSync, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
22519
+ import { mkdtempSync, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
22039
22520
  import { tmpdir } from "os";
22040
- import { join as join4 } from "path";
22041
- import { parseAllDocuments as parseYamlDocuments2, stringify as stringify2 } from "yaml";
22521
+ import { join as join5 } from "path";
22522
+ import { parseAllDocuments as parseYamlDocuments3, stringify as stringify3 } from "yaml";
22042
22523
  async function editResource(input) {
22043
22524
  const reference = parseProjectResourceReference(input.resource);
22044
22525
  const editor = resolveEditor({
@@ -22050,15 +22531,15 @@ async function editResource(input) {
22050
22531
  apiBaseUrl: input.commandOptions.apiBaseUrl
22051
22532
  });
22052
22533
  const document = editableResourceDocument(reference.kind, current);
22053
- const source = `${stringify2(document).trimEnd()}
22534
+ const source = `${stringify3(document).trimEnd()}
22054
22535
  `;
22055
- const tempRoot = mkdtempSync(join4(tmpdir(), "auto-edit-"));
22056
- const filePath = join4(tempRoot, `${reference.kind}-${reference.name}.yaml`);
22536
+ const tempRoot = mkdtempSync(join5(tmpdir(), "auto-edit-"));
22537
+ const filePath = join5(tempRoot, `${reference.kind}-${reference.name}.yaml`);
22057
22538
  writeFileSync3(filePath, source, "utf8");
22058
22539
  let removeTempFile = false;
22059
22540
  try {
22060
22541
  await runEditor(editor, filePath);
22061
- const editedSource = readFileSync4(filePath, "utf8");
22542
+ const editedSource = readFileSync5(filePath, "utf8");
22062
22543
  if (editedSource === source) {
22063
22544
  input.writeOutput("No changes; skipped apply.");
22064
22545
  removeTempFile = true;
@@ -22105,7 +22586,7 @@ function resolveEditor(input) {
22105
22586
  return input.env.VISUAL?.trim() || input.env.EDITOR?.trim() || "vi";
22106
22587
  }
22107
22588
  async function runEditor(editor, filePath) {
22108
- await new Promise((resolve2, reject) => {
22589
+ await new Promise((resolve3, reject) => {
22109
22590
  const child = spawn2(editor, [filePath], {
22110
22591
  shell: true,
22111
22592
  stdio: "inherit"
@@ -22117,7 +22598,7 @@ async function runEditor(editor, filePath) {
22117
22598
  });
22118
22599
  child.on("close", (code, signal) => {
22119
22600
  if (code === 0) {
22120
- resolve2();
22601
+ resolve3();
22121
22602
  return;
22122
22603
  }
22123
22604
  if (signal) {
@@ -22129,10 +22610,10 @@ async function runEditor(editor, filePath) {
22129
22610
  });
22130
22611
  }
22131
22612
  function readEditedResource(filePath, expected) {
22132
- const source = readFileSync4(filePath, "utf8");
22613
+ const source = readFileSync5(filePath, "utf8");
22133
22614
  let documents;
22134
22615
  try {
22135
- documents = parseYamlDocuments2(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
22616
+ documents = parseYamlDocuments3(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
22136
22617
  } catch (error51) {
22137
22618
  throw new Error(
22138
22619
  `Invalid edited resource: ${error51 instanceof Error ? error51.message : String(error51)}`
@@ -23735,7 +24216,7 @@ function ConversationView({
23735
24216
  {
23736
24217
  name: "/back",
23737
24218
  aliases: ["/exit"],
23738
- description: "Go back to the sessions dashboard",
24219
+ description: "Go back to the agents dashboard",
23739
24220
  shortcut: "esc",
23740
24221
  action: () => {
23741
24222
  setInput("");
@@ -23775,7 +24256,7 @@ function ConversationView({
23775
24256
  /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Welcome to Auto!" }),
23776
24257
  /* @__PURE__ */ jsx4(Text4, { children: " " }),
23777
24258
  /* @__PURE__ */ jsxs4(Box4, { gap: 2, children: [
23778
- /* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "Session " }),
24259
+ /* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "Agent " }),
23779
24260
  /* @__PURE__ */ jsx4(Text4, { children: sessionName })
23780
24261
  ] }),
23781
24262
  /* @__PURE__ */ jsxs4(Box4, { gap: 2, children: [
@@ -24058,13 +24539,13 @@ function sleep2(ms, signal) {
24058
24539
  if (signal.aborted) {
24059
24540
  return Promise.resolve();
24060
24541
  }
24061
- return new Promise((resolve2) => {
24062
- const timeout = setTimeout(resolve2, ms);
24542
+ return new Promise((resolve3) => {
24543
+ const timeout = setTimeout(resolve3, ms);
24063
24544
  signal.addEventListener(
24064
24545
  "abort",
24065
24546
  () => {
24066
24547
  clearTimeout(timeout);
24067
- resolve2();
24548
+ resolve3();
24068
24549
  },
24069
24550
  { once: true }
24070
24551
  );
@@ -25252,20 +25733,25 @@ var init_RunsView = __esm({
25252
25733
  // src/tui/SessionsView.tsx
25253
25734
  import { Box as Box11, Text as Text12, useStdout as useStdout6 } from "ink";
25254
25735
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
25255
- function SessionsView({ sessions, selectedIndex, error: error51 }) {
25736
+ function SessionsView({
25737
+ sessions,
25738
+ selectedIndex,
25739
+ error: error51,
25740
+ authoringStatuses
25741
+ }) {
25256
25742
  const { stdout } = useStdout6();
25257
25743
  const termWidth = stdout?.columns ?? 120;
25258
25744
  const numCols = 4;
25259
25745
  const gapTotal = (numCols - 1) * TABLE_GAP;
25260
25746
  const descriptionWidth = Math.max(
25261
25747
  24,
25262
- termWidth - TABLE_PADDING_X * 2 - COL_NAME2 - COL_RUNS - COL_UPDATED2 - gapTotal
25748
+ termWidth - TABLE_PADDING_X * 2 - COL_NAME2 - COL_AUTHORING - COL_UPDATED2 - gapTotal
25263
25749
  );
25264
25750
  return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", flexGrow: 1, children: [
25265
25751
  /* @__PURE__ */ jsxs10(Box11, { paddingX: TABLE_PADDING_X, paddingBottom: 1, gap: TABLE_GAP, children: [
25266
25752
  /* @__PURE__ */ jsx12(HeaderCell, { width: COL_NAME2, children: "NAME\u2191" }),
25267
- /* @__PURE__ */ jsx12(HeaderCell, { width: descriptionWidth, children: "DESCRIPTION" }),
25268
- /* @__PURE__ */ jsx12(HeaderCell, { width: COL_RUNS, justifyContent: "flex-end", children: "RUNS" }),
25753
+ /* @__PURE__ */ jsx12(HeaderCell, { width: descriptionWidth, children: "CONFIG" }),
25754
+ /* @__PURE__ */ jsx12(HeaderCell, { width: COL_AUTHORING, justifyContent: "flex-end", children: "AUTHORING" }),
25269
25755
  /* @__PURE__ */ jsx12(HeaderCell, { width: COL_UPDATED2, justifyContent: "flex-end", children: "UPDATED" })
25270
25756
  ] }),
25271
25757
  error51 ? /* @__PURE__ */ jsx12(Box11, { paddingX: 2, children: /* @__PURE__ */ jsxs10(Text12, { color: "red", children: [
@@ -25277,7 +25763,9 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
25277
25763
  ] }) }) : sessions.map((session, i) => {
25278
25764
  const isSelected = i === selectedIndex;
25279
25765
  const updated = relativeTime3(session.metadata.updatedAt);
25280
- const runCount = session.status.runCount.toString();
25766
+ const authoring = sessionAuthoringLabel(
25767
+ authoringStatuses?.get(session.metadata.name)
25768
+ );
25281
25769
  return /* @__PURE__ */ jsxs10(TableRow, { selected: isSelected, children: [
25282
25770
  /* @__PURE__ */ jsx12(
25283
25771
  TableCell,
@@ -25296,17 +25784,17 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
25296
25784
  width: descriptionWidth,
25297
25785
  selected: isSelected,
25298
25786
  dim: !isSelected,
25299
- children: "<todo fill in>"
25787
+ children: sessionConfigLabel(session)
25300
25788
  }
25301
25789
  ),
25302
25790
  /* @__PURE__ */ jsx12(
25303
25791
  TableCell,
25304
25792
  {
25305
- width: COL_RUNS,
25793
+ width: COL_AUTHORING,
25306
25794
  selected: isSelected,
25307
25795
  dim: !isSelected,
25308
25796
  justifyContent: "flex-end",
25309
- children: runCount
25797
+ children: authoring
25310
25798
  }
25311
25799
  ),
25312
25800
  /* @__PURE__ */ jsx12(
@@ -25323,6 +25811,26 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
25323
25811
  })
25324
25812
  ] });
25325
25813
  }
25814
+ function sessionConfigLabel(session) {
25815
+ const pieces = [
25816
+ session.spec.harness,
25817
+ session.spec.environment ? `env:${session.spec.environment}` : void 0,
25818
+ `${session.status.runCount} runs`
25819
+ ].filter(Boolean);
25820
+ return pieces.join(" ");
25821
+ }
25822
+ function sessionAuthoringLabel(status) {
25823
+ if (!status) {
25824
+ return "remote";
25825
+ }
25826
+ if (!status.ok) {
25827
+ return "error";
25828
+ }
25829
+ if (status.imports === 0 && status.removals === 0) {
25830
+ return "local ok";
25831
+ }
25832
+ return `ok i${status.imports}/r${status.removals}`;
25833
+ }
25326
25834
  function relativeTime3(dateStr) {
25327
25835
  const ms = Date.now() - new Date(dateStr).getTime();
25328
25836
  const s = Math.floor(ms / 1e3);
@@ -25334,12 +25842,12 @@ function relativeTime3(dateStr) {
25334
25842
  const d = Math.floor(h / 24);
25335
25843
  return `${d}d ago`;
25336
25844
  }
25337
- var COL_RUNS, COL_UPDATED2, COL_NAME2;
25845
+ var COL_AUTHORING, COL_UPDATED2, COL_NAME2;
25338
25846
  var init_SessionsView = __esm({
25339
25847
  "src/tui/SessionsView.tsx"() {
25340
25848
  "use strict";
25341
25849
  init_Table();
25342
- COL_RUNS = 6;
25850
+ COL_AUTHORING = 16;
25343
25851
  COL_UPDATED2 = 10;
25344
25852
  COL_NAME2 = 28;
25345
25853
  }
@@ -25347,7 +25855,7 @@ var init_SessionsView = __esm({
25347
25855
 
25348
25856
  // src/tui/SpecInspector.tsx
25349
25857
  import { Box as Box12, Text as Text13 } from "ink";
25350
- import { stringify as stringify4 } from "yaml";
25858
+ import { stringify as stringify6 } from "yaml";
25351
25859
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
25352
25860
  function SpecInspector({
25353
25861
  kind,
@@ -25378,7 +25886,7 @@ function specLineCount(spec) {
25378
25886
  return yamlLines(spec).length;
25379
25887
  }
25380
25888
  function yamlLines(spec) {
25381
- return stringify4(spec).trimEnd().split("\n");
25889
+ return stringify6(spec).trimEnd().split("\n");
25382
25890
  }
25383
25891
  function YamlLine({ line }) {
25384
25892
  const match = /^(\s*)([^:#][^:]*:)(.*)$/.exec(line);
@@ -25465,6 +25973,15 @@ function selectedNamedResourceForSection(input) {
25465
25973
  return null;
25466
25974
  }
25467
25975
  }
25976
+ function agentAuthoringHeaderLabel(status) {
25977
+ if (!status.ok) {
25978
+ return "error";
25979
+ }
25980
+ if (status.imports === 0 && status.removals === 0) {
25981
+ return "local ok";
25982
+ }
25983
+ return `local ok, ${status.imports} imports, ${status.removals} removals`;
25984
+ }
25468
25985
  function editableResourceForSelection(input) {
25469
25986
  if (!isApplyResourceSection(input.activeSection)) {
25470
25987
  return null;
@@ -25523,6 +26040,15 @@ function HomeView({ apiUrl, notice, returnToSession }) {
25523
26040
  const [serviceAccountNameInput, setServiceAccountNameInput] = useState3("");
25524
26041
  const [inspectedResource, setInspectedResource] = useState3(null);
25525
26042
  const [inspectScrollOffset, setInspectScrollOffset] = useState3(0);
26043
+ const localAgentAuthoringStatuses = useMemo3(
26044
+ () => new Map(
26045
+ readLocalAgentAuthoringStatuses().map((status) => [
26046
+ status.name,
26047
+ status
26048
+ ])
26049
+ ),
26050
+ []
26051
+ );
25526
26052
  const activeSelectionIds = client.getActiveSelection();
25527
26053
  const {
25528
26054
  data: sessions = [],
@@ -25625,6 +26151,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
25625
26151
  const selectedConnectionProvider = providerRows2(connectionProviders)[connectionProviderIndex];
25626
26152
  const selectedServiceAccount = serviceAccounts[serviceAccountIndex];
25627
26153
  const selectedSession = sessions[sessionIndex];
26154
+ const selectedAgentAuthoringStatus = selectedSession ? localAgentAuthoringStatuses.get(selectedSession.metadata.name) : void 0;
25628
26155
  const selectedEnvironment = environments[environmentIndex];
25629
26156
  const selectedIdentity = identities[identityIndex];
25630
26157
  const selectedInspectableResource = useMemo3(() => {
@@ -26505,7 +27032,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
26505
27032
  }
26506
27033
  });
26507
27034
  if (isLoading) {
26508
- return /* @__PURE__ */ jsx14(Box13, { height: termHeight, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx14(Spinner, { label: "loading sessions\u2026" }) });
27035
+ return /* @__PURE__ */ jsx14(Box13, { height: termHeight, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx14(Spinner, { label: "loading agents\u2026" }) });
26509
27036
  }
26510
27037
  if (authErrorMessage && sessions.length === 0 && !projectSelectionRequired) {
26511
27038
  return /* @__PURE__ */ jsx14(
@@ -26526,7 +27053,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
26526
27053
  let sectionTitle;
26527
27054
  switch (activeSection) {
26528
27055
  case "sessions":
26529
- sectionTitle = "Sessions";
27056
+ sectionTitle = "Agents";
26530
27057
  break;
26531
27058
  case "projects":
26532
27059
  sectionTitle = `Projects[${projects.length}]`;
@@ -26547,7 +27074,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
26547
27074
  case "runs": {
26548
27075
  const selectedSuffix = markedRunIds.length > 0 ? `, ${markedRunIds.length} selected` : "";
26549
27076
  if (scopedSession) {
26550
- sectionTitle = showArchivedRuns ? `Session Runs[${scopedSession.metadata.name}: ${visibleRuns.length} archived${selectedSuffix}]` : `Session Runs[${scopedSession.metadata.name}: ${visibleRuns.length}${selectedSuffix}]`;
27077
+ sectionTitle = showArchivedRuns ? `Agent Runs[${scopedSession.metadata.name}: ${visibleRuns.length} archived${selectedSuffix}]` : `Agent Runs[${scopedSession.metadata.name}: ${visibleRuns.length}${selectedSuffix}]`;
26551
27078
  } else {
26552
27079
  sectionTitle = showArchivedRuns ? `Runs[${visibleRuns.length} archived${selectedSuffix}]` : `Runs[${visibleRuns.length}${selectedSuffix}]`;
26553
27080
  }
@@ -26571,7 +27098,15 @@ function HomeView({ apiUrl, notice, returnToSession }) {
26571
27098
  activeProject && /* @__PURE__ */ jsx14(KV, { label: "Org", value: activeProject.organizationName }),
26572
27099
  activeProject && /* @__PURE__ */ jsx14(KV, { label: "Project", value: activeProject.projectName }),
26573
27100
  activeProject && /* @__PURE__ */ jsx14(KV, { label: "Role", value: activeProject.role }),
26574
- selectedSession && /* @__PURE__ */ jsx14(KV, { label: "Session", value: selectedSession.metadata.name }),
27101
+ selectedSession && /* @__PURE__ */ jsx14(KV, { label: "Agent", value: selectedSession.metadata.name }),
27102
+ selectedAgentAuthoringStatus && /* @__PURE__ */ jsx14(
27103
+ KV,
27104
+ {
27105
+ label: "Authoring",
27106
+ value: agentAuthoringHeaderLabel(selectedAgentAuthoringStatus),
27107
+ color: selectedAgentAuthoringStatus.ok ? void 0 : "red"
27108
+ }
27109
+ ),
26575
27110
  runError && /* @__PURE__ */ jsx14(KV, { label: "Error", value: runError, color: "red" }),
26576
27111
  isRunMutationPending && /* @__PURE__ */ jsx14(
26577
27112
  KV,
@@ -26720,7 +27255,8 @@ function HomeView({ apiUrl, notice, returnToSession }) {
26720
27255
  {
26721
27256
  sessions,
26722
27257
  selectedIndex: sessionIndex,
26723
- error: sessionsErrorMessage
27258
+ error: sessionsErrorMessage,
27259
+ authoringStatuses: localAgentAuthoringStatuses
26724
27260
  }
26725
27261
  ) : activeSection === "serviceAccounts" ? isServiceAccountsLoading ? /* @__PURE__ */ jsx14(Box13, { padding: 2, children: /* @__PURE__ */ jsx14(Spinner, { label: "loading service accounts\u2026" }) }) : /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", flexGrow: 1, children: [
26726
27262
  serviceAccountInputMode === "create" && /* @__PURE__ */ jsxs12(Box13, { paddingX: 2, gap: 1, children: [
@@ -27039,6 +27575,7 @@ var init_HomeView = __esm({
27039
27575
  "src/tui/HomeView.tsx"() {
27040
27576
  "use strict";
27041
27577
  init_src();
27578
+ init_authoring();
27042
27579
  init_login();
27043
27580
  init_base_url();
27044
27581
  init_browser();
@@ -27746,8 +28283,8 @@ async function startGitCredentialRelay(input) {
27746
28283
  update: (next) => {
27747
28284
  target = { url: next.url, accessToken: next.accessToken };
27748
28285
  },
27749
- close: () => new Promise((resolve2) => {
27750
- server.close(() => resolve2());
28286
+ close: () => new Promise((resolve3) => {
28287
+ server.close(() => resolve3());
27751
28288
  })
27752
28289
  };
27753
28290
  }
@@ -28170,12 +28707,12 @@ async function runAgentBridgeSocket(options) {
28170
28707
  let handler = null;
28171
28708
  const reconnectLoop = createReconnectLoop(socket);
28172
28709
  let hasConnected = false;
28173
- await new Promise((resolve2, reject) => {
28710
+ await new Promise((resolve3, reject) => {
28174
28711
  const shutdown = () => {
28175
28712
  reconnectLoop.stop();
28176
28713
  handler?.shutdown?.();
28177
28714
  socket.disconnect();
28178
- resolve2();
28715
+ resolve3();
28179
28716
  };
28180
28717
  process.once("SIGINT", shutdown);
28181
28718
  process.once("SIGTERM", shutdown);
@@ -28358,7 +28895,7 @@ function createRuntimeBridgeBootstrapListener(input) {
28358
28895
  };
28359
28896
  }
28360
28897
  function emitOutputWithAck(socket, output) {
28361
- return new Promise((resolve2, reject) => {
28898
+ return new Promise((resolve3, reject) => {
28362
28899
  socket.timeout(5e3).emit(
28363
28900
  RUNTIME_BRIDGE_OUTPUT_EVENT,
28364
28901
  output,
@@ -28378,7 +28915,7 @@ function emitOutputWithAck(socket, output) {
28378
28915
  );
28379
28916
  return;
28380
28917
  }
28381
- resolve2(ack.data);
28918
+ resolve3(ack.data);
28382
28919
  }
28383
28920
  );
28384
28921
  });
@@ -29121,8 +29658,8 @@ var AsyncMessageQueue = class {
29121
29658
  if (this.closed) {
29122
29659
  return Promise.resolve({ done: true, value: void 0 });
29123
29660
  }
29124
- return new Promise((resolve2) => {
29125
- this.waiters.push(resolve2);
29661
+ return new Promise((resolve3) => {
29662
+ this.waiters.push(resolve3);
29126
29663
  });
29127
29664
  }
29128
29665
  };
@@ -29386,13 +29923,13 @@ var ClaudeCodeCommandHandler = class {
29386
29923
  this.input.writeOutput?.(
29387
29924
  `agent_bridge_question_pending tool_use_id=${toolUseId}`
29388
29925
  );
29389
- return new Promise((resolve2) => {
29390
- this.pendingQuestions.set(toolUseId, { input: toolInput, resolve: resolve2 });
29926
+ return new Promise((resolve3) => {
29927
+ this.pendingQuestions.set(toolUseId, { input: toolInput, resolve: resolve3 });
29391
29928
  options.signal.addEventListener(
29392
29929
  "abort",
29393
29930
  () => {
29394
29931
  if (this.pendingQuestions.delete(toolUseId)) {
29395
- resolve2({
29932
+ resolve3({
29396
29933
  behavior: "deny",
29397
29934
  message: "The question was cancelled before the user answered",
29398
29935
  toolUseID: toolUseId
@@ -30041,7 +30578,7 @@ async function activeGrantIds(input) {
30041
30578
  );
30042
30579
  }
30043
30580
  async function waitForNewGrant(input, knownGrantIds) {
30044
- const sleep3 = input.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
30581
+ const sleep3 = input.sleep ?? ((ms) => new Promise((resolve3) => setTimeout(resolve3, ms)));
30045
30582
  const now3 = input.now ?? Date.now;
30046
30583
  const deadline = now3() + (input.pollTimeoutMs ?? POLL_TIMEOUT_MS);
30047
30584
  while (now3() < deadline) {
@@ -30738,13 +31275,13 @@ function registerDeleteCommands(program, context) {
30738
31275
 
30739
31276
  // src/commands/describe/actions.ts
30740
31277
  init_resources3();
30741
- import { stringify } from "yaml";
31278
+ import { stringify as stringify2 } from "yaml";
30742
31279
  async function inspectResource(input) {
30743
31280
  const request = parseProjectResourceReference(input.resource);
30744
31281
  const resource = await requireProjectResource(input.client, request, {
30745
31282
  apiBaseUrl: input.commandOptions.apiBaseUrl
30746
31283
  });
30747
- input.writeOutput(stringify(resource.spec).trimEnd());
31284
+ input.writeOutput(stringify2(resource.spec).trimEnd());
30748
31285
  }
30749
31286
 
30750
31287
  // src/commands/describe/commands.ts
@@ -30881,7 +31418,7 @@ var humanQuickstartText = `Get started with auto:
30881
31418
  1. auto auth login sign in (device flow; account setup in browser)
30882
31419
  2. auto connections list see available providers; auto connect <provider>
30883
31420
  3. auto apply apply .auto/ resources to your project
30884
- 4. auto run <session> launch a session run; add --attach to follow it
31421
+ 4. auto run <agent> launch an agent run; add --attach to follow it
30885
31422
 
30886
31423
  Fastest path: paste this into a coding agent running in your repo
30887
31424
  (Claude Code, Cursor, Codex):
@@ -30895,7 +31432,7 @@ Docs and help: auto --help
30895
31432
  `;
30896
31433
 
30897
31434
  // src/commands/onboard/skill-content.generated.ts
30898
- var onboardingSkillMarkdown = "# Intent\n\nYou are onboarding a user onto auto. Achieve three goals, in roughly this order, as rapidly as the user's pace allows:\n\n1. **Educate** \u2014 teach the user what auto is and how it works, and get them genuinely excited about it.\n2. **Magic moment** \u2014 get a tailor-made, deployed, proactive workflow live that solves a *real* problem for them, and have them witness it working end to end.\n3. **Self-sufficiency** \u2014 leave them with the building blocks (mental model, CI/CD, a self-improvement loop) to iterate on their auto system rapidly and safely on their own.\n\n# Background\n\n**What is auto?**\n\nauto lets you program software factories the same way you program CI/CD.\n\nCompose agents and triggers into workflows using simple YAML files, and deploy them into the cloud on merge.\n\nYou can use auto to build simple (but effective) automations:\n\n- Ticket / feedback triage and resolution\n- Automated incident / bug response\n- Custom tailored code review agents\n\nYou can also use auto to push the frontier of agentic labor:\n\n- Organized fleets of agents on long-horizon tasks\n- Multi-agent autoresearch / optimization loops\n- Agentic BDR and outbound lead engines\n- \u221E more ideas we've yet to dream up\n\nAnything that can be described in a standard operating procedure can be translated into a \"chart\" of agents and triggers in auto \u2014 the only limit is your imagination.\n\n# Reference material\n\nThis skill ships with documentation and worked examples. Read them before you onboard anyone; cite and copy from them as you go.\n\n| Path | What it covers |\n| --- | --- |\n| `docs/index.md` | The mental model: resources, events, triggers, runs. Start here. |\n| `docs/resource-model.md` | The `.auto/` directory, resource envelopes, and `auto apply` semantics. |\n| `docs/sessions-and-triggers.md` | Sessions, the trigger/event/routing vocabulary, filters, and PR checks. |\n| `docs/environments-and-profiles.md` | Sandbox images, setup steps and caching, and reusable agent profiles. |\n| `docs/tools-and-connections.md` | MCP tools, chat tools, provider connections, secrets, and the runtime tool surface agents see. |\n| `docs/cli.md` | The `auto` CLI command reference. |\n| `docs/ci-cd.md` | Service accounts and GitHub Actions for apply-on-merge. |\n| `examples/index.md` | Prose outline of every example \u2014 read this to know what's on the shelf. |\n| `examples/` | Complete, copyable `.auto/` directories \u2014 one per workflow archetype, each with a README explaining the moving parts. |\n\nIf these relative paths are not available (for example this playbook was printed by `auto onboard --agent` rather than installed as a skill directory), fetch the same content from the skills mirror: `npx skills add auto-dot-sh/skills`, or browse https://github.com/auto-dot-sh/skills.\n\n# Operating principles\n\nHold these throughout the onboarding:\n\n- **Trust live command output over this document.** The CLI evolves; run `auto --help` early and whenever in doubt, and when a command's real output disagrees with anything written here, trust the command output over this document and adapt.\n- **Converse, don't lecture.** Short messages, one question at a time, and adapt your vocabulary to the user's technical level. The pitch should take seconds, not paragraphs.\n- **Ask before changing anything outside `.auto/`.** The onboarding's write surface is the `.auto/` directory (plus the CI workflow in Beat 7, which ships as a PR). Any other file in the user's repo gets touched only with their explicit go-ahead.\n- **Warn before browsers open, and surface the link either way.** `auto auth login`, `auto connect`, and `auto sessions connect` open a browser window *and* print the authorization URL. Give a one-sentence heads-up first (\"this will open your browser to install the GitHub App\") so it doesn't feel like something hijacked their machine. If the browser doesn't pop (some environments can't open one), don't leave the user hunting through command output \u2014 repeat the printed authorization URL back to them on its own line as a clickable fallback, one provider at a time, and tell them plainly to click it.\n- **Signal before going quiet.** Deep repo exploration and waiting on async runs both involve silence. Say what you're about to do and roughly how long it will take.\n- **Enlist the user as the second pair of hands.** They trigger the inputs you can't (tagging a bot in Slack, commenting on a PR) and verify the outputs you can't see (a Slack message arriving). Make those asks explicit and specific.\n- **Hand off, don't hint.** When the user needs to do something, spell it out the *first* time \u2014 before they have to ask. Name the exact trigger (which label, which channel, which command), where to click, and what they'll see when it works. \"Label the issue whenever you're ready\" assumes they can see what's in your head and the YAML you wrote; a numbered \"in Linear: create an issue \u2192 add the `auto-triage` label \u2192 that label is the trigger\" does not. If you catch yourself about to post a one-line \"go ahead and \u2026\", expand it.\n- **Set expectations once, then stay quiet.** When you start watching an async run, tell the user up front roughly how long it takes and what \"normal\" looks like (\"the coder run provisions a sandbox first \u2014 expect a quiet couple of minutes\"), then hold until something *they'd care about* changes. Don't narrate every monitor tick or re-report the same event from a second watcher \u2014 a stream of \"still queued / still running / no news\" reads as noise, not reassurance.\n- **Expect trouble; own the troubleshooting.** OAuth flows fail, secrets get mistyped, webhooks misfire. When something breaks, diagnose it with the CLI (`auto runs list`, `auto runs show`, `auto runs conversation`, `auto apply --dry-run`) rather than asking the user to debug.\n- **Asynchronous means asynchronous.** Triggered runs take time to spawn and act. Tell the user when a wait is expected, and tail run state rather than declaring failure early.\n- **Never fabricate success.** Verify each step actually worked (the apply plan, the trigger receipt, the run conversation) before telling the user it did.\n- **Celebrate real wins.** When a workflow completes end to end for the first time, mark the moment \u2014 emoji, a pun, a little flourish. This should feel fun.\n\n# Procedure\n\nWork through the following beats in order. They are a roadmap, not a script \u2014 skip or reorder when the user's situation clearly calls for it (for example, a user who already has an account and connections can jump straight to Beat 3).\n\n## Beat 0: Learn auto\n\nBefore talking to the user, make sure you have a working command of the system: read `docs/index.md` for the mental model, skim the rest of `docs/`, and look through `examples/` to internalize what complete workflows look like. You will be drawing on the examples heavily in Beats 3-5.\n\n## Beat 1: Establish rapport\n\n**Your very first message after launching is a plain-language pitch, not a form.** Two or three sentences on what auto is and where it's valuable, then *one* opening question. Do **not** open with `AskUserQuestion` or a multiple-choice menu \u2014 that skips the *Educate* goal and makes the onboarding feel like a config wizard. Lead with words; reach for `AskUserQuestion` only once you're past the pitch and genuinely offering discrete choices (e.g. the hero workflow in Beat 3).\n\nAfter the pitch, shift into lightly interviewing the user. You want to learn:\n\n1. **Who they are and their professional context.**\n - Hobbyist, or evaluating auto for a real business?\n - How technical are they? Engineer, or a more managerial / operational role?\n2. **Where the work that matters most to them happens.**\n - Do they have a GitHub account / organization? Is there a repo that would make a good home for their auto system \u2014 better yet, are you running inside it right now?\n - Do they work out of Slack day-to-day, and could they install auto there?\n - What else is in their operating loop? Linear, Datadog, Sentry, PostHog, Notion, Telegram, internal webhooks, and so on.\n\nKeep this light \u2014 a few questions, not a survey. You're gathering enough signal to propose workflows that will land.\n\n## Beat 2: Get up to speed\n\nIf you are running inside a repo the user has indicated is their focus, tell them you're going to explore it for a few minutes (and that you'll go quiet while a research agent reads the repo) \u2014 then **dispatch a subagent to do the deep read in parallel** rather than reading file-by-file in the main thread. This keeps the conversation responsive and your own context clean, and it forces real exploration instead of leaning on whatever `CLAUDE.md` / `AGENTS.md` happened to load.\n\nSpawn one general-purpose / Explore subagent (or a small fan-out of them for a large monorepo) and have it read **both**:\n\n- **The repo:** what the project does, how the team works (CI, review culture, issue-tracker and chat integrations), the conventions written down in `CLAUDE.md`/`AGENTS.md`/`docs/`, and \u2014 most importantly \u2014 where the recurring, automatable toil is.\n- **This skill's `docs/` and `examples/`**, so the ideas it returns are already expressed in auto's vocabulary (sessions, triggers, profiles) and mapped to a concrete archetype.\n\nHave the subagent return a structured shortlist: for each candidate workflow, a one-line description, the matching archetype, the trigger/event that would fire it, and the *specific evidence in this repo* that the toil is real (a file, a workflow, a documented rule, a past incident). That shortlist is the raw material for Beat 3.\n\nWhen the agent returns, don't just move on \u2014 **surface 1-2 concrete observations to the user** (\"you renumber migrations by hand and a missed renumber caused a prod outage; your `postman/collection.json` updates are marked NOT OPTIONAL\") so they see the exploration paid off and trust that your pitches are grounded in *their* code. If `CLAUDE.md` already told you something, say so and confirm it against the repo rather than presenting it as discovery.\n\n## Beat 3: Present some options\n\nCombine what you know about the user, their goals, and their codebase, and brainstorm at least three workflows they could deploy *today*. Anchor on the archetypes in `examples/index.md` \u2014 code review, issue triage, incident response, chat assistant, scheduled digest, an orchestrated agent fleet, a research/optimization loop, an outbound lead engine \u2014 but tailor each pitch to their actual stack and pain points (\"a review agent that enforces *your* `docs/style.md`\", not \"a code review bot\"). The archetypes are anchors, not a menu: if the user's situation suggests a useful workflow that matches none of them, it is absolutely fair game \u2014 pitch it. Calibrate ambition to the user: the simple automations land the magic moment fastest, while the frontier examples (fleet, research loop) make better second acts unless the user is clearly hungry for them.\n\nPresent the options as a question, one line each on what the workflow would do for them, and let them pick \u2014 including the option to propose their own idea instead. The winner becomes the hero use case.\n\n## Beat 4: Setup & smoke test\n\nGet the user from zero to a deployed, *hollow* version of the hero workflow \u2014 a shell that proves every input and output is wired up before you invest in the real logic. In practice:\n\n1. **Install the CLI**: `npm install -g @autohq/cli` (requires Node 20+). Verify with `auto --version`.\n2. **Sign in**: `auto auth login` (heads-up: opens a browser; account creation happens there too). You're blocked on the user completing the flow either way, so wait for them \u2014 don't busy yourself with other work mid-sign-in, which only confuses things. When you're driving from a terminal with no browser, `auto auth login --device` prints a code the user enters in their browser.\n3. **Create the org and project**: `auto orgs create` / `auto projects create`. Ask the user what they want to name them \u2014 don't pick names for them.\n4. **Connect providers**: `auto connections list --available` to see what's offered, then `auto connect <provider>` for each one the workflow needs (heads-up: browser again). GitHub connects as an App installation; Slack and Linear as OAuth grants.\n5. **Scaffold `.auto/`**: create the directory in their repo and draft the minimal resources \u2014 an environment, a profile, any tool definitions, and a session with the workflow's trigger. Copy from the matching example and strip it down.\n6. **Apply**: `auto apply --dry-run` first, show the user the plan, then `auto apply`.\n\nThen run the smoke test. Its exact shape depends on the use case, but the goal is always the same: verify that the trigger fires and the agent's output surfaces reach the user. A workflow almost always involves some communication channel, so a good smoke test \"breaks the fourth wall\" \u2014 have the hollow agent send the user a hello in Slack (or wherever they live).\n\nEnlist the user, and **hand off, don't hint** (see the operating principle): when you ask them to fire the input only they can fire, give the full, numbered steps the first time \u2014 *which* label on *which* issue, *which* channel to create, the exact command to run, and what they'll see when it lands. Don't post \"go ahead and label the issue\" and assume they know a label is the trigger; that one-liner is what makes a user ask \"wait, what exactly do I do?\". Right after `auto apply`, before you start watching, tell them in plain words what just deployed and what their next action is. Then **set expectations once** \u2014 \"the run takes a minute or two to spawn; I'll tell you when it acts\" \u2014 and watch progress yourself with `auto runs list` and `auto attach <run-id>` (live stream; `auto runs conversation <run-id>` for a snapshot), surfacing only meaningful changes rather than every tick. Troubleshoot until the smoke test passes.\n\nIf a channel install is blocked \u2014 for example the Slack workspace requires admin approval \u2014 don't stall the onboarding on it. Pick an output surface the user can verify without the channel (a PR comment, a GitHub check, the run transcript via `auto runs conversation`), continue the beats, and circle back to realize the channel identity once the approval lands.\n\n## Beat 5: Build the real thing\n\nWith inputs and outputs proven, flesh the workflow out to its real form in `.auto/` \u2014 the full profile instructions, the real prompt, the filters and routing that make it production-shaped. Tell the user what you're changing, then apply it.\n\nTest end to end: trigger the workflow for real, follow the run, and enlist the user again for out-of-band inputs and output verification. Iterate until you've witnessed one complete, successful run of the real workflow.\n\nThen celebrate. This is the magic moment \u2014 act like it. \u{1F389}\n\n## Beat 6: Bring the user up to speed\n\nWalk the user through what you built, piece by piece: which environment, profile, tools, session, and triggers you composed, how an event flows through them to become a run, and where each file lives in `.auto/`. Show short snippets from the actual files rather than describing them abstractly.\n\nThen ask: anything they want to dig into further, or shall we set up CI/CD?\n\n## Beat 7: Set up CI/CD\n\nMake merges to their default branch the deployment mechanism for their auto system (this is the \"program software factories like CI/CD\" promise made literal). Following `docs/ci-cd.md`:\n\n1. Create a service account: have the *user* run `auto service-account create ci-apply --preset applier` in their own terminal (and a second `--preset read-only` account for PR dry-runs if they want plan-on-PR). The token prints exactly once and goes straight into a repo secret \u2014 it must never be pasted into the conversation, and if you run the command yourself it lands in your transcript.\n2. Add a GitHub Actions workflow that runs `auto apply --dry-run` on pull requests and `auto apply` on pushes to the default branch.\n3. Tell the user exactly which secret to create where in their repo settings (the service-account token, shown once at creation).\n4. Open a PR containing `.auto/` and the new workflow, and ask the user to merge it.\n\nWhen the merge lands, verify the apply ran cleanly in Actions, and congratulate them \u2014 their factory now ships itself.\n\n## Beat 8: Set up a self-improvement loop\n\nTell the user there's one last step we've found high-leverage: a workflow that watches their auto system itself \u2014 sweeping recent runs for failures, bottlenecks, and drift, and proposing improvements. Explain that it's just another auto workflow, fully theirs to tune.\n\nIf they're in, copy `examples/self-improvement/` and tailor it to their setup (their channel, their sessions, their cadence). Since CI/CD is now live, do **not** run `auto apply` yourself \u2014 open a PR and let them merge it. That's the new normal, and modeling it is the point.\n\n## Beat 9: Conclusion\n\nTell the user they're all set: a live workflow, CI/CD for their auto system, and a loop that helps it improve. Recap in two or three lines what now exists. Offer to help them build or optimize additional workflows \u2014 Beat 3's runner-up ideas are natural next candidates.\n";
31435
+ var onboardingSkillMarkdown = "# Intent\n\nYou are onboarding a user onto auto. Achieve three goals, in roughly this order, as rapidly as the user's pace allows:\n\n1. **Educate** \u2014 teach the user what auto is and how it works, and get them genuinely excited about it.\n2. **Magic moment** \u2014 get a tailor-made, deployed, proactive workflow live that solves a *real* problem for them, and have them witness it working end to end.\n3. **Self-sufficiency** \u2014 leave them with the building blocks (mental model, CI/CD, a self-improvement loop) to iterate on their auto system rapidly and safely on their own.\n\n# Background\n\n**What is auto?**\n\nauto lets you program software factories the same way you program CI/CD.\n\nCompose agents and triggers into workflows using simple YAML files, and deploy them into the cloud on merge.\n\nYou can use auto to build simple (but effective) automations:\n\n- Ticket / feedback triage and resolution\n- Automated incident / bug response\n- Custom tailored code review agents\n\nYou can also use auto to push the frontier of agentic labor:\n\n- Organized fleets of agents on long-horizon tasks\n- Multi-agent autoresearch / optimization loops\n- Agentic BDR and outbound lead engines\n- \u221E more ideas we've yet to dream up\n\nAnything that can be described in a standard operating procedure can be translated into a \"chart\" of agents and triggers in auto \u2014 the only limit is your imagination.\n\n# Reference material\n\nThis skill ships with documentation and worked examples. Read them before you onboard anyone; cite and copy from them as you go.\n\n| Path | What it covers |\n| --- | --- |\n| `docs/index.md` | The mental model: resources, events, triggers, runs. Start here. |\n| `docs/resource-model.md` | The `.auto/` directory, resource envelopes, and `auto apply` semantics. |\n| `docs/sessions-and-triggers.md` | Agents, the trigger/event/routing vocabulary, filters, and PR checks. |\n| `docs/environments-and-profiles.md` | Sandbox images, setup steps and caching, and reusable agent profiles. |\n| `docs/tools-and-connections.md` | MCP tools, chat tools, provider connections, secrets, and the runtime tool surface agents see. |\n| `docs/cli.md` | The `auto` CLI command reference. |\n| `docs/ci-cd.md` | Service accounts and GitHub Actions for apply-on-merge. |\n| `examples/index.md` | Prose outline of every example \u2014 read this to know what's on the shelf. |\n| `examples/` | Complete, copyable `.auto/` directories \u2014 one per workflow archetype, each with a README explaining the moving parts. |\n\nIf these relative paths are not available (for example this playbook was printed by `auto onboard --agent` rather than installed as a skill directory), fetch the same content from the skills mirror: `npx skills add auto-dot-sh/skills`, or browse https://github.com/auto-dot-sh/skills.\n\n# Operating principles\n\nHold these throughout the onboarding:\n\n- **Trust live command output over this document.** The CLI evolves; run `auto --help` early and whenever in doubt, and when a command's real output disagrees with anything written here, trust the command output over this document and adapt.\n- **Converse, don't lecture.** Short messages, one question at a time, and adapt your vocabulary to the user's technical level. The pitch should take seconds, not paragraphs.\n- **Ask before changing anything outside `.auto/`.** The onboarding's write surface is the `.auto/` directory (plus the CI workflow in Beat 7, which ships as a PR). Any other file in the user's repo gets touched only with their explicit go-ahead.\n- **Warn before browsers open, and surface the link either way.** `auto auth login`, `auto connect`, and `auto agents connect` open a browser window *and* print the authorization URL. Give a one-sentence heads-up first (\"this will open your browser to install the GitHub App\") so it doesn't feel like something hijacked their machine. If the browser doesn't pop (some environments can't open one), don't leave the user hunting through command output \u2014 repeat the printed authorization URL back to them on its own line as a clickable fallback, one provider at a time, and tell them plainly to click it.\n- **Signal before going quiet.** Deep repo exploration and waiting on async runs both involve silence. Say what you're about to do and roughly how long it will take.\n- **Enlist the user as the second pair of hands.** They trigger the inputs you can't (tagging a bot in Slack, commenting on a PR) and verify the outputs you can't see (a Slack message arriving). Make those asks explicit and specific.\n- **Hand off, don't hint.** When the user needs to do something, spell it out the *first* time \u2014 before they have to ask. Name the exact trigger (which label, which channel, which command), where to click, and what they'll see when it works. \"Label the issue whenever you're ready\" assumes they can see what's in your head and the YAML you wrote; a numbered \"in Linear: create an issue \u2192 add the `auto-triage` label \u2192 that label is the trigger\" does not. If you catch yourself about to post a one-line \"go ahead and \u2026\", expand it.\n- **Set expectations once, then stay quiet.** When you start watching an async run, tell the user up front roughly how long it takes and what \"normal\" looks like (\"the coder run provisions a sandbox first \u2014 expect a quiet couple of minutes\"), then hold until something *they'd care about* changes. Don't narrate every monitor tick or re-report the same event from a second watcher \u2014 a stream of \"still queued / still running / no news\" reads as noise, not reassurance.\n- **Expect trouble; own the troubleshooting.** OAuth flows fail, secrets get mistyped, webhooks misfire. When something breaks, diagnose it with the CLI (`auto runs list`, `auto runs show`, `auto runs conversation`, `auto apply --dry-run`) rather than asking the user to debug.\n- **Asynchronous means asynchronous.** Triggered runs take time to spawn and act. Tell the user when a wait is expected, and tail run state rather than declaring failure early.\n- **Never fabricate success.** Verify each step actually worked (the apply plan, the trigger receipt, the run conversation) before telling the user it did.\n- **Celebrate real wins.** When a workflow completes end to end for the first time, mark the moment \u2014 emoji, a pun, a little flourish. This should feel fun.\n\n# Procedure\n\nWork through the following beats in order. They are a roadmap, not a script \u2014 skip or reorder when the user's situation clearly calls for it (for example, a user who already has an account and connections can jump straight to Beat 3).\n\n## Beat 0: Learn auto\n\nBefore talking to the user, make sure you have a working command of the system: read `docs/index.md` for the mental model, skim the rest of `docs/`, and look through `examples/` to internalize what complete workflows look like. You will be drawing on the examples heavily in Beats 3-5.\n\n## Beat 1: Establish rapport\n\n**Your very first message after launching is a plain-language pitch, not a form.** Two or three sentences on what auto is and where it's valuable, then *one* opening question. Do **not** open with `AskUserQuestion` or a multiple-choice menu \u2014 that skips the *Educate* goal and makes the onboarding feel like a config wizard. Lead with words; reach for `AskUserQuestion` only once you're past the pitch and genuinely offering discrete choices (e.g. the hero workflow in Beat 3).\n\nAfter the pitch, shift into lightly interviewing the user. You want to learn:\n\n1. **Who they are and their professional context.**\n - Hobbyist, or evaluating auto for a real business?\n - How technical are they? Engineer, or a more managerial / operational role?\n2. **Where the work that matters most to them happens.**\n - Do they have a GitHub account / organization? Is there a repo that would make a good home for their auto system \u2014 better yet, are you running inside it right now?\n - Do they work out of Slack day-to-day, and could they install auto there?\n - What else is in their operating loop? Linear, Datadog, Sentry, PostHog, Notion, Telegram, internal webhooks, and so on.\n\nKeep this light \u2014 a few questions, not a survey. You're gathering enough signal to propose workflows that will land.\n\n## Beat 2: Get up to speed\n\nIf you are running inside a repo the user has indicated is their focus, tell them you're going to explore it for a few minutes (and that you'll go quiet while a research agent reads the repo) \u2014 then **dispatch a subagent to do the deep read in parallel** rather than reading file-by-file in the main thread. This keeps the conversation responsive and your own context clean, and it forces real exploration instead of leaning on whatever `CLAUDE.md` / `AGENTS.md` happened to load.\n\nSpawn one general-purpose / Explore subagent (or a small fan-out of them for a large monorepo) and have it read **both**:\n\n- **The repo:** what the project does, how the team works (CI, review culture, issue-tracker and chat integrations), the conventions written down in `CLAUDE.md`/`AGENTS.md`/`docs/`, and \u2014 most importantly \u2014 where the recurring, automatable toil is.\n- **This skill's `docs/` and `examples/`**, so the ideas it returns are already expressed in auto's vocabulary (agents, triggers, profiles) and mapped to a concrete archetype.\n\nHave the subagent return a structured shortlist: for each candidate workflow, a one-line description, the matching archetype, the trigger/event that would fire it, and the *specific evidence in this repo* that the toil is real (a file, a workflow, a documented rule, a past incident). That shortlist is the raw material for Beat 3.\n\nWhen the agent returns, don't just move on \u2014 **surface 1-2 concrete observations to the user** (\"you renumber migrations by hand and a missed renumber caused a prod outage; your `postman/collection.json` updates are marked NOT OPTIONAL\") so they see the exploration paid off and trust that your pitches are grounded in *their* code. If `CLAUDE.md` already told you something, say so and confirm it against the repo rather than presenting it as discovery.\n\n## Beat 3: Present some options\n\nCombine what you know about the user, their goals, and their codebase, and brainstorm at least three workflows they could deploy *today*. Anchor on the archetypes in `examples/index.md` \u2014 code review, issue triage, incident response, chat assistant, scheduled digest, an orchestrated agent fleet, a research/optimization loop, an outbound lead engine \u2014 but tailor each pitch to their actual stack and pain points (\"a review agent that enforces *your* `docs/style.md`\", not \"a code review bot\"). The archetypes are anchors, not a menu: if the user's situation suggests a useful workflow that matches none of them, it is absolutely fair game \u2014 pitch it. Calibrate ambition to the user: the simple automations land the magic moment fastest, while the frontier examples (fleet, research loop) make better second acts unless the user is clearly hungry for them.\n\nPresent the options as a question, one line each on what the workflow would do for them, and let them pick \u2014 including the option to propose their own idea instead. The winner becomes the hero use case.\n\n## Beat 4: Setup & smoke test\n\nGet the user from zero to a deployed, *hollow* version of the hero workflow \u2014 a shell that proves every input and output is wired up before you invest in the real logic. In practice:\n\n1. **Install the CLI**: `npm install -g @autohq/cli` (requires Node 20+). Verify with `auto --version`.\n2. **Sign in**: `auto auth login` (heads-up: opens a browser; account creation happens there too). You're blocked on the user completing the flow either way, so wait for them \u2014 don't busy yourself with other work mid-sign-in, which only confuses things. When you're driving from a terminal with no browser, `auto auth login --device` prints a code the user enters in their browser.\n3. **Create the org and project**: `auto orgs create` / `auto projects create`. Ask the user what they want to name them \u2014 don't pick names for them.\n4. **Connect providers**: `auto connections list --available` to see what's offered, then `auto connect <provider>` for each one the workflow needs (heads-up: browser again). GitHub connects as an App installation; Slack and Linear as OAuth grants.\n5. **Scaffold `.auto/`**: create the directory in their repo and draft the minimal resources \u2014 an environment, a profile, any tool definitions, and an agent with the workflow's trigger. Copy from the matching example and strip it down.\n6. **Apply**: `auto apply --dry-run` first, show the user the plan, then `auto apply`.\n\nThen run the smoke test. Its exact shape depends on the use case, but the goal is always the same: verify that the trigger fires and the agent's output surfaces reach the user. A workflow almost always involves some communication channel, so a good smoke test \"breaks the fourth wall\" \u2014 have the hollow agent send the user a hello in Slack (or wherever they live).\n\nEnlist the user, and **hand off, don't hint** (see the operating principle): when you ask them to fire the input only they can fire, give the full, numbered steps the first time \u2014 *which* label on *which* issue, *which* channel to create, the exact command to run, and what they'll see when it lands. Don't post \"go ahead and label the issue\" and assume they know a label is the trigger; that one-liner is what makes a user ask \"wait, what exactly do I do?\". Right after `auto apply`, before you start watching, tell them in plain words what just deployed and what their next action is. Then **set expectations once** \u2014 \"the run takes a minute or two to spawn; I'll tell you when it acts\" \u2014 and watch progress yourself with `auto runs list` and `auto attach <run-id>` (live stream; `auto runs conversation <run-id>` for a snapshot), surfacing only meaningful changes rather than every tick. Troubleshoot until the smoke test passes.\n\nIf a channel install is blocked \u2014 for example the Slack workspace requires admin approval \u2014 don't stall the onboarding on it. Pick an output surface the user can verify without the channel (a PR comment, a GitHub check, the run transcript via `auto runs conversation`), continue the beats, and circle back to realize the channel identity once the approval lands.\n\n## Beat 5: Build the real thing\n\nWith inputs and outputs proven, flesh the workflow out to its real form in `.auto/` \u2014 the full profile instructions, the real prompt, the filters and routing that make it production-shaped. Tell the user what you're changing, then apply it.\n\nTest end to end: trigger the workflow for real, follow the run, and enlist the user again for out-of-band inputs and output verification. Iterate until you've witnessed one complete, successful run of the real workflow.\n\nThen celebrate. This is the magic moment \u2014 act like it. \u{1F389}\n\n## Beat 6: Bring the user up to speed\n\nWalk the user through what you built, piece by piece: which environment, profile, tools, agent, and triggers you composed, how an event flows through them to become a run, and where each file lives in `.auto/`. Show short snippets from the actual files rather than describing them abstractly.\n\nThen ask: anything they want to dig into further, or shall we set up CI/CD?\n\n## Beat 7: Set up CI/CD\n\nMake merges to their default branch the deployment mechanism for their auto system (this is the \"program software factories like CI/CD\" promise made literal). Following `docs/ci-cd.md`:\n\n1. Create a service account: have the *user* run `auto service-account create ci-apply --preset applier` in their own terminal (and a second `--preset read-only` account for PR dry-runs if they want plan-on-PR). The token prints exactly once and goes straight into a repo secret \u2014 it must never be pasted into the conversation, and if you run the command yourself it lands in your transcript.\n2. Add a GitHub Actions workflow that runs `auto apply --dry-run` on pull requests and `auto apply` on pushes to the default branch.\n3. Tell the user exactly which secret to create where in their repo settings (the service-account token, shown once at creation).\n4. Open a PR containing `.auto/` and the new workflow, and ask the user to merge it.\n\nWhen the merge lands, verify the apply ran cleanly in Actions, and congratulate them \u2014 their factory now ships itself.\n\n## Beat 8: Set up a self-improvement loop\n\nTell the user there's one last step we've found high-leverage: a workflow that watches their auto system itself \u2014 sweeping recent runs for failures, bottlenecks, and drift, and proposing improvements. Explain that it's just another auto workflow, fully theirs to tune.\n\nIf they're in, copy `examples/self-improvement/` and tailor it to their setup (their channel, their agents, their cadence). Since CI/CD is now live, do **not** run `auto apply` yourself \u2014 open a PR and let them merge it. That's the new normal, and modeling it is the point.\n\n## Beat 9: Conclusion\n\nTell the user they're all set: a live workflow, CI/CD for their auto system, and a loop that helps it improve. Recap in two or three lines what now exists. Offer to help them build or optimize additional workflows \u2014 Beat 3's runner-up ideas are natural next candidates.\n";
30899
31436
 
30900
31437
  // src/commands/onboard/commands.ts
30901
31438
  function registerOnboardCommands(program, context) {
@@ -31421,13 +31958,13 @@ function delay(ms, signal) {
31421
31958
  if (signal.aborted) {
31422
31959
  return Promise.resolve();
31423
31960
  }
31424
- return new Promise((resolve2) => {
31425
- const timeout = setTimeout(resolve2, ms);
31961
+ return new Promise((resolve3) => {
31962
+ const timeout = setTimeout(resolve3, ms);
31426
31963
  signal.addEventListener(
31427
31964
  "abort",
31428
31965
  () => {
31429
31966
  clearTimeout(timeout);
31430
- resolve2();
31967
+ resolve3();
31431
31968
  },
31432
31969
  { once: true }
31433
31970
  );
@@ -31452,11 +31989,11 @@ function pollUntilFailed(input) {
31452
31989
  input.abort?.addEventListener("abort", cancel, { once: true });
31453
31990
  const done = (async () => {
31454
31991
  while (!cancelled) {
31455
- await new Promise((resolve2) => {
31456
- sleepResolve = resolve2;
31992
+ await new Promise((resolve3) => {
31993
+ sleepResolve = resolve3;
31457
31994
  activeSleep = setTimeout(() => {
31458
31995
  activeSleep = void 0;
31459
- resolve2();
31996
+ resolve3();
31460
31997
  }, interval);
31461
31998
  });
31462
31999
  if (cancelled) {
@@ -31793,7 +32330,7 @@ function delay2(ms) {
31793
32330
  if (ms <= 0) {
31794
32331
  return Promise.resolve();
31795
32332
  }
31796
- return new Promise((resolve2) => setTimeout(resolve2, ms));
32333
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
31797
32334
  }
31798
32335
  function createDiagnosticCollector(limit) {
31799
32336
  const events = [];
@@ -32191,9 +32728,9 @@ function entryPreview(event, full) {
32191
32728
  case "text":
32192
32729
  return part.text;
32193
32730
  case "tool_call":
32194
- return `${part.name} ${stringify3(part.input)}`;
32731
+ return `${part.name} ${stringify4(part.input)}`;
32195
32732
  case "tool_result":
32196
- return stringify3(part.output);
32733
+ return stringify4(part.output);
32197
32734
  case "question":
32198
32735
  return part.questions.map((question) => question.question).join(" ");
32199
32736
  default:
@@ -32204,9 +32741,9 @@ function entryPreview(event, full) {
32204
32741
  return full ? joined : clip(joined);
32205
32742
  }
32206
32743
  function preview(value) {
32207
- return clip(singleLine(stringify3(value)));
32744
+ return clip(singleLine(stringify4(value)));
32208
32745
  }
32209
- function stringify3(value) {
32746
+ function stringify4(value) {
32210
32747
  return typeof value === "string" ? value : JSON.stringify(value);
32211
32748
  }
32212
32749
  function singleLine(text) {
@@ -32221,7 +32758,7 @@ function collect(value, previous = []) {
32221
32758
  return [...previous, value];
32222
32759
  }
32223
32760
  function registerRunCommands(program, context) {
32224
- const runs = program.command("runs").description("Inspect session runs.");
32761
+ const runs = program.command("runs").description("Inspect agent runs.");
32225
32762
  runs.command("list").description("List runs for a session or the whole project.").option("--session <name>", "session name").option("--include-archived", "include archived runs").option("--status <status>", "filter by run status (repeatable)", collect).option("--since <iso-timestamp>", "only runs created after this time").option("--limit <count>", "maximum runs to return", parsePositiveInteger).option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (options) => {
32226
32763
  await handleRunsList(context, options.session, options);
32227
32764
  });
@@ -32291,7 +32828,7 @@ function registerRunCommands(program, context) {
32291
32828
  runs.command("stop").description("Stop live runs and shut down their agent sessions.").argument("<run-ids...>", "run id(s)").option("--reason <reason>", "reason recorded with the stop").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (runIds, commandOptions) => {
32292
32829
  await stopRunsAction(context, runIds, commandOptions);
32293
32830
  });
32294
- runs.command("benchmark-startup").description("Launch a session run and measure time until awaiting.").requiredOption("--session <name>", "session name").option("-m, --message <message>", "initial message for the run").option(
32831
+ runs.command("benchmark-startup").description("Launch an agent run and measure time until awaiting.").requiredOption("--session <name>", "agent name").option("-m, --message <message>", "initial message for the run").option(
32295
32832
  "--interactive",
32296
32833
  "start without the default initial prompt unless --message is provided"
32297
32834
  ).option(
@@ -32313,7 +32850,7 @@ function registerRunCommands(program, context) {
32313
32850
  ).option("--json", "write a single JSON result object").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (options) => {
32314
32851
  await benchmarkStartupAction(context, options);
32315
32852
  });
32316
- program.command("interactive").description("Launch a session run, stream it, and send typed messages.").argument("<session>", "session name").option("-m, --message <message>", "initial message for the run").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").option("--operator <name>", "operator name for attach/detach messages").option(
32853
+ program.command("interactive").description("Launch an agent run, stream it, and send typed messages.").argument("<session>", "agent name").option("-m, --message <message>", "initial message for the run").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").option("--operator <name>", "operator name for attach/detach messages").option(
32317
32854
  "--attach-message",
32318
32855
  "send the automated attach message after creating the run"
32319
32856
  ).option("--no-attach-message", "do not send the automated attach message").option("--no-detach-message", "do not send the automated detach message").action(
@@ -32651,12 +33188,57 @@ function withApiBaseUrl2(context, commandOptions) {
32651
33188
  };
32652
33189
  }
32653
33190
 
33191
+ // src/commands/agents/actions.ts
33192
+ init_authoring();
33193
+ import { stringify as stringify5 } from "yaml";
33194
+ function renderAgent(input) {
33195
+ const result = compileAgent(input);
33196
+ input.writeOutput(renderCompiledAgentYaml(result));
33197
+ }
33198
+ function compileAgent(input) {
33199
+ const path2 = resolveAgentAuthoringPath({
33200
+ agent: input.agent,
33201
+ directory: input.options.directory
33202
+ });
33203
+ return compileAgentFile(path2);
33204
+ }
33205
+ function compileAgentCommand(input) {
33206
+ const result = compileAgent(input);
33207
+ input.writeOutput(
33208
+ input.options.json ? JSON.stringify(result.resource) : stringify5(result.resource).trimEnd()
33209
+ );
33210
+ }
33211
+ function explainAgent(input) {
33212
+ const result = compileAgent(input);
33213
+ input.writeOutput(
33214
+ input.options.json ? JSON.stringify(result) : renderAgentExplain(result)
33215
+ );
33216
+ }
33217
+ function statusAgents(input) {
33218
+ const statuses = readLocalAgentAuthoringStatuses({
33219
+ directory: input.options.directory
33220
+ });
33221
+ if (input.options.json) {
33222
+ input.writeOutput(JSON.stringify(statuses));
33223
+ return;
33224
+ }
33225
+ if (statuses.length === 0) {
33226
+ input.writeOutput("No local agent authoring files found.");
33227
+ return;
33228
+ }
33229
+ for (const status of statuses) {
33230
+ const state = status.ok ? "ok" : "error";
33231
+ const suffix = status.ok ? `imports=${status.imports} removals=${status.removals}` : status.error;
33232
+ input.writeOutput(`${state} ${status.name} ${suffix}`);
33233
+ }
33234
+ }
33235
+
32654
33236
  // src/commands/sessions/connect.ts
32655
33237
  init_resources2();
32656
33238
  init_browser();
32657
33239
  import { existsSync as existsSync3, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync4 } from "fs";
32658
33240
  import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
32659
- import { join as join5 } from "path";
33241
+ import { join as join6 } from "path";
32660
33242
  var POLL_INTERVAL_MS2 = 2e3;
32661
33243
  var POLL_TIMEOUT_MS2 = 5 * 6e4;
32662
33244
  var SLACK_APPS_URL = "https://api.slack.com/apps";
@@ -32677,7 +33259,7 @@ async function connectSessionPresence2(input) {
32677
33259
  }
32678
33260
  for (const pending of result.pending) {
32679
33261
  input.writeOutput(
32680
- `connect session/${input.session} workspace/${pending.workspace} connection/${pending.connection}`
33262
+ `connect agent/${input.session} workspace/${pending.workspace} connection/${pending.connection}`
32681
33263
  );
32682
33264
  input.writeOutput(`authorization_url ${pending.authorizationUrl}`);
32683
33265
  if (pending.suggestedUsername) {
@@ -32708,7 +33290,7 @@ async function connectSessionPresence2(input) {
32708
33290
  );
32709
33291
  return;
32710
33292
  }
32711
- const sleep3 = input.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
33293
+ const sleep3 = input.sleep ?? ((ms) => new Promise((resolve3) => setTimeout(resolve3, ms)));
32712
33294
  const openBrowser2 = input.openBrowser ?? openBrowser;
32713
33295
  const now3 = input.now ?? Date.now;
32714
33296
  const pollIntervalMs = input.pollIntervalMs ?? POLL_INTERVAL_MS2;
@@ -32738,7 +33320,7 @@ async function connectSessionPresence2(input) {
32738
33320
  }
32739
33321
  if (!realized) {
32740
33322
  throw new Error(
32741
- telegram ? `Timed out waiting for the Telegram bot to be confirmed under ${pending.workspace}. Confirm the creation dialog in Telegram, then re-run \`auto sessions connect ${input.session}\`.` : `Timed out waiting for the install in workspace ${pending.workspace}. Re-run \`auto sessions connect ${input.session}\` to retry.`
33323
+ telegram ? `Timed out waiting for the Telegram bot to be confirmed under ${pending.workspace}. Confirm the creation dialog in Telegram, then re-run \`auto agents connect ${input.session}\`.` : `Timed out waiting for the install in workspace ${pending.workspace}. Re-run \`auto agents connect ${input.session}\` to retry.`
32742
33324
  );
32743
33325
  }
32744
33326
  }
@@ -32746,19 +33328,19 @@ async function connectSessionPresence2(input) {
32746
33328
  }
32747
33329
  function realizedIdentityLine(session, identity2) {
32748
33330
  const handle = identity2.botUsername ? `persona/@${identity2.botUsername}` : `bot/${identity2.botUserId ?? ""}`;
32749
- return `connected session/${session} workspace/${identity2.workspace} ${handle}`;
33331
+ return `connected agent/${session} workspace/${identity2.workspace} ${handle}`;
32750
33332
  }
32751
33333
  function staleScopesLine(session, identity2) {
32752
33334
  if (!identity2.missingScopes?.length) {
32753
33335
  return void 0;
32754
33336
  }
32755
- return ` workspace "${identity2.workspace}" was installed before scopes ${identity2.missingScopes.join(", ")} were added; refresh with \`auto sessions connect ${session} --reconnect\``;
33337
+ return ` workspace "${identity2.workspace}" was installed before scopes ${identity2.missingScopes.join(", ")} were added; refresh with \`auto agents connect ${session} --reconnect\``;
32756
33338
  }
32757
33339
  function manualGuidance(input) {
32758
33340
  if (input.allTelegram) {
32759
- return `Open each creation link and confirm the new bot in Telegram; Auto provisions it automatically. Re-run \`auto sessions connect ${input.session}\` (or check presence) to see it realize.`;
33341
+ return `Open each creation link and confirm the new bot in Telegram; Auto provisions it automatically. Re-run \`auto agents connect ${input.session}\` (or check presence) to see it realize.`;
32760
33342
  }
32761
- return input.reconnect ? "Open each authorization URL to reinstall the agent app; the callback page confirms the refreshed tokens." : "Open each authorization URL to install the agent app, then re-run with no flags or check `sessions connect` again.";
33343
+ return input.reconnect ? "Open each authorization URL to reinstall the agent app; the callback page confirms the refreshed tokens." : "Open each authorization URL to install the agent app, then re-run with no flags or check `agents connect` again.";
32762
33344
  }
32763
33345
  async function promptForIconUploads(input, options) {
32764
33346
  const presence = await input.client.getSessionPresence(
@@ -32775,14 +33357,14 @@ async function promptForIconUploads(input, options) {
32775
33357
  for (const identity2 of drifted) {
32776
33358
  const settingsUrl = `${SLACK_APPS_URL}/${identity2.appId}/general`;
32777
33359
  input.writeOutput(
32778
- `Workspace "${identity2.workspace}" agent app icon does not match the session avatar yet. Slack has no app-icon API; upload the avatar image once under "Display Information" at ${settingsUrl}`
33360
+ `Workspace "${identity2.workspace}" agent app icon does not match the agent avatar yet. Slack has no app-icon API; upload the avatar image once under "Display Information" at ${settingsUrl}`
32779
33361
  );
32780
33362
  if (identity2.avatarUrl) {
32781
33363
  input.writeOutput(`avatar image: ${identity2.avatarUrl}`);
32782
33364
  }
32783
33365
  if (!canPrompt) {
32784
33366
  input.writeOutput(
32785
- `Then re-run \`auto sessions connect ${input.session}\` interactively to record the upload.`
33367
+ `Then re-run \`auto agents connect ${input.session}\` interactively to record the upload.`
32786
33368
  );
32787
33369
  continue;
32788
33370
  }
@@ -32822,12 +33404,12 @@ async function promptForIconUploads(input, options) {
32822
33404
  options
32823
33405
  );
32824
33406
  input.writeOutput(
32825
- `recorded icon session/${input.session} workspace/${identity2.workspace} sha256/${recorded.appliedIconSha256.slice(0, 12)}`
33407
+ `recorded icon agent/${input.session} workspace/${identity2.workspace} sha256/${recorded.appliedIconSha256.slice(0, 12)}`
32826
33408
  );
32827
33409
  }
32828
33410
  }
32829
33411
  function stagedLocationLabel(stagedPath) {
32830
- return stagedPath.startsWith(`${join5(homedir3(), "Downloads")}/`) ? "Downloads" : "the printed path";
33412
+ return stagedPath.startsWith(`${join6(homedir3(), "Downloads")}/`) ? "Downloads" : "the printed path";
32831
33413
  }
32832
33414
  async function stageAvatarImage(input) {
32833
33415
  try {
@@ -32839,9 +33421,9 @@ async function stageAvatarImage(input) {
32839
33421
  }
32840
33422
  const contentType = response.headers.get("content-type") ?? "";
32841
33423
  const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
32842
- const downloads = join5(homedir3(), "Downloads");
32843
- const directory = existsSync3(downloads) ? downloads : mkdtempSync2(join5(tmpdir2(), "auto-avatar-"));
32844
- const path2 = join5(directory, `${input.session}-avatar${extension}`);
33424
+ const downloads = join6(homedir3(), "Downloads");
33425
+ const directory = existsSync3(downloads) ? downloads : mkdtempSync2(join6(tmpdir2(), "auto-avatar-"));
33426
+ const path2 = join6(directory, `${input.session}-avatar${extension}`);
32845
33427
  writeFileSync4(path2, Buffer.from(await response.arrayBuffer()));
32846
33428
  return path2;
32847
33429
  } catch {
@@ -32929,17 +33511,56 @@ async function launchRun(input) {
32929
33511
 
32930
33512
  // src/commands/sessions/commands.ts
32931
33513
  function registerSessionCommands(program, context) {
32932
- program.command("run").description("Launch a session run on the Auto platform.").argument("<session>", "session name").option("-m, --message <message>", "initial message for the run").option("-a, --attach", "stream the run conversation after launch").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (sessionName, commandOptions) => {
33514
+ program.command("run").description("Launch an agent run on the Auto platform.").argument("<session>", "agent name").option("-m, --message <message>", "initial message for the run").option("-a, --attach", "stream the run conversation after launch").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (sessionName, commandOptions) => {
32933
33515
  await launchRun({
32934
33516
  sessionName,
32935
33517
  commandOptions,
32936
33518
  context
32937
33519
  });
32938
33520
  });
32939
- const sessions = program.command("sessions").description("Manage Session resources.");
33521
+ const sessions = program.command("agents").alias("sessions").description("Manage Agent resources.");
33522
+ sessions.command("render").description("Render a local Agent authoring file after imports/removals.").argument("<agent>", "agent name or path").option(
33523
+ "--directory <directory>",
33524
+ "directory containing Auto resource files"
33525
+ ).action((agent, commandOptions) => {
33526
+ renderAgent({
33527
+ agent,
33528
+ options: commandOptions,
33529
+ writeOutput: context.writeOutput
33530
+ });
33531
+ });
33532
+ sessions.command("compile").description("Compile a local Agent authoring file into an apply resource.").argument("<agent>", "agent name or path").option(
33533
+ "--directory <directory>",
33534
+ "directory containing Auto resource files"
33535
+ ).option("--json", "print the compiled resource as JSON").action((agent, commandOptions) => {
33536
+ compileAgentCommand({
33537
+ agent,
33538
+ options: commandOptions,
33539
+ writeOutput: context.writeOutput
33540
+ });
33541
+ });
33542
+ sessions.command("explain").description("Explain a local Agent authoring file's import graph.").argument("<agent>", "agent name or path").option(
33543
+ "--directory <directory>",
33544
+ "directory containing Auto resource files"
33545
+ ).option("--json", "print the import graph as JSON").action((agent, commandOptions) => {
33546
+ explainAgent({
33547
+ agent,
33548
+ options: commandOptions,
33549
+ writeOutput: context.writeOutput
33550
+ });
33551
+ });
33552
+ sessions.command("status").description("Validate local Agent authoring files.").option(
33553
+ "--directory <directory>",
33554
+ "directory containing Auto resource files"
33555
+ ).option("--json", "print local authoring status as JSON").action((commandOptions) => {
33556
+ statusAgents({
33557
+ options: commandOptions,
33558
+ writeOutput: context.writeOutput
33559
+ });
33560
+ });
32940
33561
  sessions.command("connect").description(
32941
- "Connect a session's provider presence (e.g. install its Slack agent app)."
32942
- ).argument("<session>", "session resource name").option("--manual", "print authorization URLs without opening a browser").option(
33562
+ "Connect an agent's provider presence (e.g. install its Slack agent app)."
33563
+ ).argument("<session>", "agent resource name").option("--manual", "print authorization URLs without opening a browser").option(
32943
33564
  "--reconnect",
32944
33565
  "re-run the install for already-connected workspaces (repairs stranded bot tokens)"
32945
33566
  ).option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(
@@ -32955,9 +33576,7 @@ function registerSessionCommands(program, context) {
32955
33576
  });
32956
33577
  }
32957
33578
  );
32958
- program.command("run-and-attach").description(
32959
- "Launch a session run and immediately stream its conversation."
32960
- ).argument("<session>", "session name").option("-m, --message <message>", "initial message for the run").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (sessionName, commandOptions) => {
33579
+ program.command("run-and-attach").description("Launch an agent run and immediately stream its conversation.").argument("<session>", "agent name").option("-m, --message <message>", "initial message for the run").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (sessionName, commandOptions) => {
32961
33580
  await launchRun({
32962
33581
  sessionName,
32963
33582
  commandOptions: {