@autohq/cli 0.1.134 → 0.1.136

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"() {
@@ -16191,7 +16191,8 @@ var init_auth = __esm({
16191
16191
  "environments:read",
16192
16192
  "tools:read",
16193
16193
  "sessions:read",
16194
- "runs:read"
16194
+ "runs:read",
16195
+ "provider_grants:read"
16195
16196
  ];
16196
16197
  SERVICE_ACCOUNT_SCOPE_PRESETS = {
16197
16198
  "read-only": SERVICE_ACCOUNT_READ_ONLY_SCOPES,
@@ -16199,7 +16200,8 @@ var init_auth = __esm({
16199
16200
  ...SERVICE_ACCOUNT_READ_ONLY_SCOPES,
16200
16201
  "environments:write",
16201
16202
  "tools:write",
16202
- "sessions:write"
16203
+ "sessions:write",
16204
+ "provider_grants:use"
16203
16205
  ]
16204
16206
  };
16205
16207
  ServiceAccountScopePresetSchema = external_exports.enum(["read-only", "applier"]);
@@ -17159,7 +17161,7 @@ var init_resources = __esm({
17159
17161
  });
17160
17162
 
17161
17163
  // ../../packages/schemas/src/connections.ts
17162
- var ConnectionNameSchema, ProviderConnectionReferenceSchema, GithubConnectionAccountSchema, GithubConnectionRepositorySchema, GITHUB_CONNECTION_EVENTS, GithubConnectionEventSchema, GithubConnectionSpecSchema, ConnectionSpecSchema, ConnectionResourceSchema, ConnectionApplyRequestSchema, ConnectionStartRequestSchema, ConnectionProviderDescriptorSchema, ConnectionProviderListResponseSchema, ConnectionStartResponseSchema, TelegramConnectionCreateRequestSchema, TelegramManagerBotSummarySchema, TelegramConnectionCreateResponseSchema, ConnectionAllowRequestSchema, SlackConfigTokenRegisterRequestSchema, SlackConfigTokenRegisterResponseSchema, ConnectionAllowResponseSchema, ConnectionRemoveRequestSchema, ConnectionRemoveResponseSchema;
17164
+ var RESOURCE_KIND_CONNECTION, ConnectionNameSchema, ProviderConnectionReferenceSchema, ProjectConnectionAllocationSpecSchema, ProjectConnectionAllocationResourceSchema, GithubConnectionAccountSchema, GithubConnectionRepositorySchema, GITHUB_CONNECTION_EVENTS, GithubConnectionEventSchema, GithubConnectionSpecSchema, ConnectionSpecSchema, ConnectionResourceSchema, ConnectionApplyRequestSchema, ConnectionStartRequestSchema, ConnectionProviderDescriptorSchema, ConnectionProviderListResponseSchema, ConnectionStartResponseSchema, TelegramConnectionCreateRequestSchema, TelegramManagerBotSummarySchema, TelegramConnectionCreateResponseSchema, ConnectionAllowRequestSchema, SlackConfigTokenRegisterRequestSchema, SlackConfigTokenRegisterResponseSchema, ConnectionAllowResponseSchema, ConnectionRemoveRequestSchema, ConnectionRemoveResponseSchema;
17163
17165
  var init_connections = __esm({
17164
17166
  "../../packages/schemas/src/connections.ts"() {
17165
17167
  "use strict";
@@ -17167,11 +17169,18 @@ var init_connections = __esm({
17167
17169
  init_ids();
17168
17170
  init_provider_grants();
17169
17171
  init_resources();
17172
+ RESOURCE_KIND_CONNECTION = "connection";
17170
17173
  ConnectionNameSchema = ResourceNameSchema;
17171
17174
  ProviderConnectionReferenceSchema = external_exports.object({
17172
17175
  provider: ProviderNameSchema,
17173
17176
  connection: ConnectionNameSchema
17174
17177
  });
17178
+ ProjectConnectionAllocationSpecSchema = external_exports.object({
17179
+ provider: ProviderNameSchema
17180
+ });
17181
+ ProjectConnectionAllocationResourceSchema = resourceEnvelopeSchema(
17182
+ ProjectConnectionAllocationSpecSchema
17183
+ );
17175
17184
  GithubConnectionAccountSchema = external_exports.object({
17176
17185
  login: external_exports.string().trim().min(1),
17177
17186
  type: external_exports.enum(["User", "Organization"])
@@ -18595,11 +18604,12 @@ function normalizeLegacySessionKind(value) {
18595
18604
  function isRecord(value) {
18596
18605
  return typeof value === "object" && value !== null && !Array.isArray(value);
18597
18606
  }
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;
18607
+ var EnvironmentApplyDocumentSchema, IdentityApplyDocumentSchema, SessionApplyDocumentSchema, ProjectApplyResourceSchema, PROJECT_RESOURCE_APPLY_ORDER, PROJECT_RESOURCE_KINDS, PROJECT_APPLY_RESOURCE_KINDS, ProjectDeleteResourceBaseSchema, ProjectDeleteResourceSchema, AVATAR_ASSET_CONTENT_TYPES, MAX_AVATAR_ASSET_BASE64_LENGTH, ProjectApplyAssetSchema, ProjectApplyAssetsSchema, AvatarAssetUploadRequestSchema, AvatarAssetUploadResponseSchema, ProjectApplyRequestSchema, ProjectApplySystemConfigSchema, ProjectAppliedResourceSchema, ProjectApplyDiagnosticSchema, ProjectApplyResponseResourceKindSchema, ProjectApplyResponseSchema;
18599
18608
  var init_project_resources = __esm({
18600
18609
  "../../packages/schemas/src/project-resources.ts"() {
18601
18610
  "use strict";
18602
18611
  init_zod();
18612
+ init_connections();
18603
18613
  init_environments();
18604
18614
  init_identities();
18605
18615
  init_resources();
@@ -18625,18 +18635,24 @@ var init_project_resources = __esm({
18625
18635
  ])
18626
18636
  );
18627
18637
  PROJECT_RESOURCE_APPLY_ORDER = [
18628
- RESOURCE_KIND_ENVIRONMENT,
18638
+ RESOURCE_KIND_CONNECTION,
18629
18639
  RESOURCE_KIND_IDENTITY,
18640
+ RESOURCE_KIND_ENVIRONMENT,
18630
18641
  RESOURCE_KIND_SESSION
18631
18642
  ];
18632
18643
  PROJECT_RESOURCE_KINDS = PROJECT_RESOURCE_APPLY_ORDER;
18644
+ PROJECT_APPLY_RESOURCE_KINDS = [
18645
+ RESOURCE_KIND_IDENTITY,
18646
+ RESOURCE_KIND_ENVIRONMENT,
18647
+ RESOURCE_KIND_SESSION
18648
+ ];
18633
18649
  ProjectDeleteResourceBaseSchema = external_exports.object({
18634
18650
  name: external_exports.string().trim().min(1)
18635
18651
  });
18636
18652
  ProjectDeleteResourceSchema = external_exports.preprocess(
18637
18653
  normalizeLegacySessionKind,
18638
18654
  ProjectDeleteResourceBaseSchema.extend({
18639
- kind: external_exports.enum(PROJECT_RESOURCE_KINDS)
18655
+ kind: external_exports.enum(PROJECT_APPLY_RESOURCE_KINDS)
18640
18656
  })
18641
18657
  );
18642
18658
  AVATAR_ASSET_CONTENT_TYPES = ["image/png", "image/jpeg"];
@@ -18675,6 +18691,7 @@ var init_project_resources = __esm({
18675
18691
  spec: ProjectApplyRequestSchema
18676
18692
  });
18677
18693
  ProjectAppliedResourceSchema = external_exports.union([
18694
+ ProjectConnectionAllocationResourceSchema,
18678
18695
  EnvironmentResourceSchema,
18679
18696
  IdentityResourceSchema,
18680
18697
  SessionResourceSchema
@@ -18686,6 +18703,7 @@ var init_project_resources = __esm({
18686
18703
  message: external_exports.string().min(1),
18687
18704
  action: external_exports.enum(["create", "update", "unchanged"]),
18688
18705
  kind: external_exports.enum([
18706
+ RESOURCE_KIND_CONNECTION,
18689
18707
  RESOURCE_KIND_ENVIRONMENT,
18690
18708
  RESOURCE_KIND_IDENTITY,
18691
18709
  RESOURCE_KIND_SESSION
@@ -21208,7 +21226,7 @@ var init_package = __esm({
21208
21226
  "package.json"() {
21209
21227
  package_default = {
21210
21228
  name: "@autohq/cli",
21211
- version: "0.1.134",
21229
+ version: "0.1.136",
21212
21230
  license: "SEE LICENSE IN README.md",
21213
21231
  publishConfig: {
21214
21232
  access: "public"
@@ -21375,14 +21393,8 @@ var init_assets = __esm({
21375
21393
  }
21376
21394
  });
21377
21395
 
21378
- // src/commands/apply/files.ts
21379
- import { createHash as createHash3 } from "crypto";
21380
- import {
21381
- readFileSync as readFileSync3,
21382
- readdirSync as readdirSync2,
21383
- realpathSync,
21384
- statSync
21385
- } from "fs";
21396
+ // src/commands/agents/authoring.ts
21397
+ import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync } from "fs";
21386
21398
  import {
21387
21399
  basename as basename2,
21388
21400
  dirname as dirname4,
@@ -21391,7 +21403,436 @@ import {
21391
21403
  join as join3,
21392
21404
  resolve
21393
21405
  } from "path";
21394
- import { parseAllDocuments as parseYamlDocuments } from "yaml";
21406
+ import { parseAllDocuments as parseYamlDocuments, stringify } from "yaml";
21407
+ function compileAgentFile(path2) {
21408
+ const context = { graph: /* @__PURE__ */ new Map(), removals: [] };
21409
+ const document = readSingleDocument(path2);
21410
+ const compiled = normalizeAgentDocument(
21411
+ compileAgentDocument(document, path2, [], context)
21412
+ );
21413
+ const parsed = ProjectApplyResourceSchema.safeParse(compiled);
21414
+ if (!parsed.success) {
21415
+ throw new Error(`Invalid compiled agent ${path2}: ${parsed.error.message}`);
21416
+ }
21417
+ if (parsed.data.kind !== RESOURCE_KIND_SESSION) {
21418
+ throw new Error(
21419
+ `Invalid compiled agent ${path2}: expected kind "agent", got "${parsed.data.kind}"`
21420
+ );
21421
+ }
21422
+ return {
21423
+ resource: parsed.data,
21424
+ graph: [...context.graph.values()],
21425
+ removals: context.removals
21426
+ };
21427
+ }
21428
+ function compileAgentDocumentValue(document, path2) {
21429
+ const context = { graph: /* @__PURE__ */ new Map(), removals: [] };
21430
+ const compiled = normalizeAgentDocument(
21431
+ compileAgentDocument(document, path2, [], context)
21432
+ );
21433
+ const parsed = ProjectApplyResourceSchema.safeParse(compiled);
21434
+ if (!parsed.success) {
21435
+ throw new Error(`Invalid compiled agent ${path2}: ${parsed.error.message}`);
21436
+ }
21437
+ if (parsed.data.kind !== RESOURCE_KIND_SESSION) {
21438
+ throw new Error(
21439
+ `Invalid compiled agent ${path2}: expected kind "agent", got "${parsed.data.kind}"`
21440
+ );
21441
+ }
21442
+ return {
21443
+ resource: parsed.data,
21444
+ graph: [...context.graph.values()],
21445
+ removals: context.removals
21446
+ };
21447
+ }
21448
+ function renderCompiledAgentYaml(result) {
21449
+ return stringify(result.resource).trimEnd();
21450
+ }
21451
+ function renderAgentExplain(result) {
21452
+ const lines = [
21453
+ `agent ${result.resource.metadata.name}`,
21454
+ `files ${result.graph.length}`
21455
+ ];
21456
+ for (const node of result.graph) {
21457
+ lines.push(node.path);
21458
+ for (const imported of node.imports) {
21459
+ lines.push(` imports ${imported}`);
21460
+ }
21461
+ }
21462
+ if (result.removals.length > 0) {
21463
+ lines.push("removals");
21464
+ for (const removal of result.removals) {
21465
+ lines.push(
21466
+ ` ${removal.target}: ${removal.names.join(", ")} (${removal.path})`
21467
+ );
21468
+ }
21469
+ }
21470
+ return lines.join("\n");
21471
+ }
21472
+ function readLocalAgentAuthoringStatuses(input) {
21473
+ const agentsDirectory = join3(
21474
+ input?.directory ?? join3(process.cwd(), ".auto"),
21475
+ "agents"
21476
+ );
21477
+ let entries;
21478
+ try {
21479
+ entries = readdirSync2(agentsDirectory).filter(
21480
+ (entry) => AGENT_FILE_EXTENSIONS.includes(
21481
+ extname(
21482
+ entry
21483
+ ).toLowerCase()
21484
+ )
21485
+ ).sort((left, right) => left.localeCompare(right));
21486
+ } catch {
21487
+ return [];
21488
+ }
21489
+ return entries.map((entry) => {
21490
+ const path2 = join3(agentsDirectory, entry);
21491
+ const fallbackName = basename2(entry, extname(entry));
21492
+ try {
21493
+ const result = compileAgentFile(path2);
21494
+ return {
21495
+ name: result.resource.metadata.name,
21496
+ path: path2,
21497
+ ok: true,
21498
+ imports: importedEdgeCount(result.graph),
21499
+ removals: result.removals.reduce(
21500
+ (count, removal) => count + removal.names.length,
21501
+ 0
21502
+ )
21503
+ };
21504
+ } catch (error51) {
21505
+ return {
21506
+ name: fallbackName,
21507
+ path: path2,
21508
+ ok: false,
21509
+ imports: 0,
21510
+ removals: 0,
21511
+ error: error51 instanceof Error ? error51.message : String(error51)
21512
+ };
21513
+ }
21514
+ });
21515
+ }
21516
+ function importedAgentAuthoringPaths(paths) {
21517
+ const imported = /* @__PURE__ */ new Set();
21518
+ for (const path2 of paths) {
21519
+ for (const document of readDocuments(path2)) {
21520
+ discoverImports(document, path2, [], imported);
21521
+ }
21522
+ }
21523
+ return imported;
21524
+ }
21525
+ function resolveAgentAuthoringPath(input) {
21526
+ const candidate = resolve(input.agent);
21527
+ if (isAgentFile(candidate)) {
21528
+ return candidate;
21529
+ }
21530
+ const agentsDirectory = join3(
21531
+ input.directory ?? join3(process.cwd(), ".auto"),
21532
+ "agents"
21533
+ );
21534
+ for (const extension of AGENT_FILE_EXTENSIONS) {
21535
+ const path2 = join3(agentsDirectory, `${input.agent}${extension}`);
21536
+ if (isAgentFile(path2)) {
21537
+ return path2;
21538
+ }
21539
+ }
21540
+ throw new Error(
21541
+ `Agent authoring file not found for "${input.agent}" under ${agentsDirectory}`
21542
+ );
21543
+ }
21544
+ function discoverImports(document, path2, stack, imported) {
21545
+ const resolvedPath = resolve(path2);
21546
+ if (stack.includes(resolvedPath)) {
21547
+ throw new Error(
21548
+ `Agent import cycle detected: ${[...stack, resolvedPath].join(" -> ")}`
21549
+ );
21550
+ }
21551
+ if (!isRecord2(document)) {
21552
+ return;
21553
+ }
21554
+ for (const importPath of importPaths(document)) {
21555
+ const resolvedImport = resolveImportPath(importPath, resolvedPath);
21556
+ imported.add(resolvedImport);
21557
+ discoverImports(
21558
+ readSingleDocument(resolvedImport),
21559
+ resolvedImport,
21560
+ [...stack, resolvedPath],
21561
+ imported
21562
+ );
21563
+ }
21564
+ }
21565
+ function compileAgentDocument(document, path2, stack, context) {
21566
+ const resolvedPath = resolve(path2);
21567
+ if (stack.includes(resolvedPath)) {
21568
+ throw new Error(
21569
+ `Agent import cycle detected: ${[...stack, resolvedPath].join(" -> ")}`
21570
+ );
21571
+ }
21572
+ if (!isRecord2(document)) {
21573
+ throw new Error(`Invalid agent authoring file ${path2}: expected object`);
21574
+ }
21575
+ const imports = importPaths(document).map(
21576
+ (importPath) => resolveImportPath(importPath, resolvedPath)
21577
+ );
21578
+ context.graph.set(resolvedPath, { path: resolvedPath, imports });
21579
+ let merged = {};
21580
+ for (const imported of imports) {
21581
+ const importedDocument = readSingleDocument(imported);
21582
+ merged = mergeValues2(
21583
+ merged,
21584
+ compileAgentDocument(
21585
+ importedDocument,
21586
+ imported,
21587
+ [...stack, resolvedPath],
21588
+ context
21589
+ ),
21590
+ []
21591
+ );
21592
+ }
21593
+ const removals = removalDirectives(document, resolvedPath);
21594
+ for (const removal of removals) {
21595
+ merged = applyRemoval(merged, removal);
21596
+ context.removals.push(removal);
21597
+ }
21598
+ return mergeValues2(merged, sanitizedAgentDocument(document), []);
21599
+ }
21600
+ function readSingleDocument(path2) {
21601
+ const documents = readDocuments(path2);
21602
+ if (documents.length !== 1) {
21603
+ throw new Error(
21604
+ `Agent authoring file ${path2} must contain exactly one document`
21605
+ );
21606
+ }
21607
+ return documents[0];
21608
+ }
21609
+ function readDocuments(path2) {
21610
+ const source = readFileSync3(path2, "utf8");
21611
+ return parseYamlDocuments(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
21612
+ }
21613
+ function importPaths(document) {
21614
+ const value = document.imports ?? document.import;
21615
+ if (value === void 0) {
21616
+ return [];
21617
+ }
21618
+ const values = Array.isArray(value) ? value : [value];
21619
+ return values.map((item) => {
21620
+ if (typeof item !== "string" || item.trim().length === 0) {
21621
+ throw new Error("Agent imports must be non-empty string paths");
21622
+ }
21623
+ return item.trim();
21624
+ });
21625
+ }
21626
+ function resolveImportPath(importPath, importerPath) {
21627
+ if (isAbsolute(importPath) || /^[A-Za-z]+:\/\//.test(importPath)) {
21628
+ throw new Error(`Agent import must be a relative path: ${importPath}`);
21629
+ }
21630
+ const resolved = resolve(dirname4(importerPath), importPath);
21631
+ if (!isAgentFile(resolved)) {
21632
+ throw new Error(
21633
+ `Agent import not found: ${importPath} from ${importerPath}`
21634
+ );
21635
+ }
21636
+ return resolved;
21637
+ }
21638
+ function sanitizedAgentDocument(document) {
21639
+ const next = { ...document };
21640
+ delete next.import;
21641
+ delete next.imports;
21642
+ delete next.remove;
21643
+ if (isRecord2(next.spec) && "remove" in next.spec) {
21644
+ const spec = { ...next.spec };
21645
+ delete spec.remove;
21646
+ next.spec = spec;
21647
+ }
21648
+ return next;
21649
+ }
21650
+ function normalizeAgentDocument(document) {
21651
+ if (!isRecord2(document) || "kind" in document) {
21652
+ return document;
21653
+ }
21654
+ return { ...document, kind: RESOURCE_KIND_SESSION };
21655
+ }
21656
+ function removalDirectives(document, path2) {
21657
+ const raw = mergeValues2(
21658
+ isRecord2(document.remove) ? document.remove : {},
21659
+ isRecord2(document.spec) && isRecord2(document.spec.remove) ? document.spec.remove : {},
21660
+ []
21661
+ );
21662
+ if (!isRecord2(raw)) {
21663
+ return [];
21664
+ }
21665
+ const directives = [];
21666
+ for (const [target, value] of Object.entries(raw)) {
21667
+ const names = stringList(value, `remove.${target}`);
21668
+ if (names.length > 0) {
21669
+ directives.push({ path: path2, target, names });
21670
+ }
21671
+ }
21672
+ return directives;
21673
+ }
21674
+ function stringList(value, label) {
21675
+ const values = Array.isArray(value) ? value : [value];
21676
+ return values.map((item) => {
21677
+ if (typeof item !== "string" || item.trim().length === 0) {
21678
+ throw new Error(`${label} entries must be non-empty strings`);
21679
+ }
21680
+ return item.trim();
21681
+ });
21682
+ }
21683
+ function applyRemoval(value, removal) {
21684
+ if (!isRecord2(value)) {
21685
+ return value;
21686
+ }
21687
+ const next = structuredClone(value);
21688
+ if (!isRecord2(next.spec)) {
21689
+ return next;
21690
+ }
21691
+ const spec = { ...next.spec };
21692
+ next.spec = spec;
21693
+ switch (removal.target) {
21694
+ case "tools":
21695
+ case "env": {
21696
+ if (!isRecord2(spec[removal.target])) {
21697
+ return next;
21698
+ }
21699
+ const collection = {
21700
+ ...spec[removal.target]
21701
+ };
21702
+ for (const name of removal.names) {
21703
+ delete collection[name];
21704
+ }
21705
+ spec[removal.target] = collection;
21706
+ return next;
21707
+ }
21708
+ case "mounts":
21709
+ case "triggers": {
21710
+ const value2 = spec[removal.target];
21711
+ if (!Array.isArray(value2)) {
21712
+ return next;
21713
+ }
21714
+ const collection = value2;
21715
+ spec[removal.target] = collection.filter(
21716
+ (item) => !removal.names.includes(
21717
+ collectionItemKey(removal.target, item) ?? ""
21718
+ )
21719
+ );
21720
+ return next;
21721
+ }
21722
+ default:
21723
+ throw new Error(
21724
+ `Unsupported agent remove target "${removal.target}"; supported targets are tools, env, mounts, triggers`
21725
+ );
21726
+ }
21727
+ }
21728
+ function mergeValues2(base, override, path2) {
21729
+ if (override === void 0) {
21730
+ return base;
21731
+ }
21732
+ if (Array.isArray(base) && Array.isArray(override)) {
21733
+ return mergeArrays(base, override, path2);
21734
+ }
21735
+ if (isRecord2(base) && isRecord2(override)) {
21736
+ const merged = { ...base };
21737
+ for (const [key, value] of Object.entries(override)) {
21738
+ merged[key] = mergeValues2(merged[key], value, [...path2, key]);
21739
+ }
21740
+ return merged;
21741
+ }
21742
+ return override;
21743
+ }
21744
+ function mergeArrays(base, override, path2) {
21745
+ const pathKey = path2.join(".");
21746
+ if (pathKey !== "spec.mounts" && pathKey !== "spec.triggers") {
21747
+ return [...base, ...override];
21748
+ }
21749
+ const merged = [...base];
21750
+ const indexByKey = /* @__PURE__ */ new Map();
21751
+ for (const [index, item] of merged.entries()) {
21752
+ const key = collectionItemKey(path2.at(-1) ?? "", item);
21753
+ if (key) {
21754
+ indexByKey.set(key, index);
21755
+ }
21756
+ }
21757
+ for (const item of override) {
21758
+ const key = collectionItemKey(path2.at(-1) ?? "", item);
21759
+ if (key && indexByKey.has(key)) {
21760
+ merged[indexByKey.get(key) ?? 0] = mergeValues2(
21761
+ merged[indexByKey.get(key) ?? 0],
21762
+ item,
21763
+ path2
21764
+ );
21765
+ continue;
21766
+ }
21767
+ if (key) {
21768
+ indexByKey.set(key, merged.length);
21769
+ }
21770
+ merged.push(item);
21771
+ }
21772
+ return merged;
21773
+ }
21774
+ function collectionItemKey(collection, item) {
21775
+ if (!isRecord2(item)) {
21776
+ return void 0;
21777
+ }
21778
+ if (typeof item.name === "string") {
21779
+ return item.name;
21780
+ }
21781
+ if (collection === "mounts" && typeof item.mountPath === "string") {
21782
+ return item.mountPath;
21783
+ }
21784
+ if (collection === "triggers") {
21785
+ if (typeof item.event === "string") {
21786
+ return item.event;
21787
+ }
21788
+ if (Array.isArray(item.events)) {
21789
+ return item.events.filter((event) => typeof event === "string").join(",");
21790
+ }
21791
+ if (typeof item.cron === "string") {
21792
+ return `cron:${item.cron}:${typeof item.timezone === "string" ? item.timezone : ""}`;
21793
+ }
21794
+ }
21795
+ return void 0;
21796
+ }
21797
+ function importedEdgeCount(graph) {
21798
+ return graph.reduce((count, node) => count + node.imports.length, 0);
21799
+ }
21800
+ function isAgentFile(path2) {
21801
+ try {
21802
+ return statSync(path2).isFile();
21803
+ } catch {
21804
+ return false;
21805
+ }
21806
+ }
21807
+ function isRecord2(value) {
21808
+ return typeof value === "object" && value !== null && !Array.isArray(value);
21809
+ }
21810
+ var AGENT_FILE_EXTENSIONS;
21811
+ var init_authoring = __esm({
21812
+ "src/commands/agents/authoring.ts"() {
21813
+ "use strict";
21814
+ init_src();
21815
+ AGENT_FILE_EXTENSIONS = [".yaml", ".yml", ".json"];
21816
+ }
21817
+ });
21818
+
21819
+ // src/commands/apply/files.ts
21820
+ import { createHash as createHash3 } from "crypto";
21821
+ import {
21822
+ readFileSync as readFileSync4,
21823
+ readdirSync as readdirSync3,
21824
+ realpathSync,
21825
+ statSync as statSync2
21826
+ } from "fs";
21827
+ import {
21828
+ basename as basename3,
21829
+ dirname as dirname5,
21830
+ extname as extname2,
21831
+ isAbsolute as isAbsolute2,
21832
+ join as join4,
21833
+ resolve as resolve2
21834
+ } from "path";
21835
+ import { parseAllDocuments as parseYamlDocuments2 } from "yaml";
21395
21836
  function readProjectApplyRequest(options) {
21396
21837
  if (options.file && options.directory) {
21397
21838
  throw new Error("Cannot use --file with --directory.");
@@ -21404,7 +21845,7 @@ function readProjectApplyRequest(options) {
21404
21845
  );
21405
21846
  return { ...request, assets: assets2 };
21406
21847
  }
21407
- const directory = options.directory ?? join3(process.cwd(), ".auto");
21848
+ const directory = options.directory ?? join4(process.cwd(), ".auto");
21408
21849
  const files = applyFiles(directory);
21409
21850
  if (files.length === 0) {
21410
21851
  throw new Error(`No resource files found in ${directory}`);
@@ -21455,25 +21896,31 @@ function mcpOAuthSessionToolConnectionsFromAppliedResources(resources) {
21455
21896
  }
21456
21897
  function applyFiles(root) {
21457
21898
  const files = [];
21458
- for (const kind of PROJECT_RESOURCE_APPLY_ORDER) {
21899
+ for (const kind of PROJECT_APPLY_RESOURCE_KINDS) {
21900
+ const kindFiles = [];
21459
21901
  for (const directory of applyDirectories(kind)) {
21460
- const path2 = join3(root, directory);
21902
+ const path2 = join4(root, directory);
21461
21903
  let entries;
21462
21904
  try {
21463
- entries = readdirSync2(path2, { withFileTypes: true });
21905
+ entries = readdirSync3(path2, { withFileTypes: true });
21464
21906
  } catch {
21465
21907
  continue;
21466
21908
  }
21467
- files.push(
21468
- ...resourceApplyFiles(path2, entries).map((file2) => ({
21469
- kind,
21470
- path: file2
21471
- }))
21472
- );
21909
+ kindFiles.push(...resourceApplyFiles(path2, entries));
21473
21910
  }
21911
+ const importedFiles = kind === RESOURCE_KIND_SESSION ? importedAgentAuthoringPaths(kindFiles) : /* @__PURE__ */ new Set();
21912
+ const appliedFiles = kind === RESOURCE_KIND_SESSION ? kindFiles.filter(
21913
+ (path2) => !importedFiles.has(resolve2(path2)) && !isSharedAgentAuthoringFile(root, path2)
21914
+ ) : kindFiles;
21915
+ files.push(...appliedFiles.map((path2) => ({ kind, path: path2 })));
21474
21916
  }
21475
21917
  return files;
21476
21918
  }
21919
+ function isSharedAgentAuthoringFile(root, path2) {
21920
+ const agentsSharedRoot = resolve2(root, "agents", "shared");
21921
+ const resolvedPath = resolve2(path2);
21922
+ return resolvedPath.startsWith(`${agentsSharedRoot}/`);
21923
+ }
21477
21924
  function mcpOAuthSessionToolConnectionsFromSessionTools(input) {
21478
21925
  return Object.entries(input.tools).flatMap(([alias, tool]) => {
21479
21926
  if (tool.kind !== "mcp_remote" || tool.disabled || tool.auth.kind !== "mcp_oauth") {
@@ -21489,10 +21936,10 @@ function mcpOAuthSessionToolConnectionsFromSessionTools(input) {
21489
21936
  });
21490
21937
  }
21491
21938
  function readApplyDocumentFile(path2) {
21492
- const source = readFileSync3(path2, "utf8");
21939
+ const source = readFileSync4(path2, "utf8");
21493
21940
  let documents;
21494
21941
  try {
21495
- documents = parseYamlDocuments(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
21942
+ documents = parseYamlDocuments2(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
21496
21943
  } catch (error51) {
21497
21944
  throw new Error(
21498
21945
  `Invalid apply file: ${error51 instanceof Error ? error51.message : String(error51)}`
@@ -21511,12 +21958,15 @@ function readApplyDocumentFile(path2) {
21511
21958
  delete: [],
21512
21959
  dryRun: false,
21513
21960
  prune: false,
21514
- resources: documents.map(readApplyDocument),
21961
+ resources: documents.map((document) => readApplyDocument(document, path2)),
21515
21962
  assets: {}
21516
21963
  };
21517
21964
  }
21518
- function readApplyDocument(document) {
21965
+ function readApplyDocument(document, path2) {
21519
21966
  const candidate = applyCandidate(document);
21967
+ if (candidate.kind === RESOURCE_KIND_SESSION) {
21968
+ return compileAgentDocumentValue(document, path2).resource;
21969
+ }
21520
21970
  const parsed = APPLY_SCHEMAS[candidate.kind].safeParse(candidate.value);
21521
21971
  if (!parsed.success) {
21522
21972
  throw new Error(
@@ -21541,7 +21991,7 @@ function readApplyAssets(resources, projectRoot) {
21541
21991
  resourceName: target.resourceName,
21542
21992
  projectRoot
21543
21993
  });
21544
- const bytes = readFileSync3(resolvedPath);
21994
+ const bytes = readFileSync4(resolvedPath);
21545
21995
  const dimensionsProblem = avatarAssetDimensionsProblem(bytes);
21546
21996
  if (dimensionsProblem) {
21547
21997
  throw new Error(
@@ -21550,7 +22000,7 @@ function readApplyAssets(resources, projectRoot) {
21550
22000
  }
21551
22001
  assets[target.asset] = {
21552
22002
  sha256: createHash3("sha256").update(bytes).digest("hex"),
21553
- contentType: extname(resolvedPath).toLowerCase() === ".png" ? "image/png" : "image/jpeg",
22003
+ contentType: extname2(resolvedPath).toLowerCase() === ".png" ? "image/png" : "image/jpeg",
21554
22004
  dataBase64: bytes.toString("base64")
21555
22005
  };
21556
22006
  }
@@ -21568,7 +22018,7 @@ function avatarAssetTarget(resource) {
21568
22018
  return void 0;
21569
22019
  }
21570
22020
  function validateSessionAvatarAsset(input) {
21571
- if (isAbsolute(input.asset)) {
22021
+ if (isAbsolute2(input.asset)) {
21572
22022
  throw new Error(
21573
22023
  `Invalid identity avatar asset for "${input.resourceName}": asset path must be relative`
21574
22024
  );
@@ -21580,10 +22030,10 @@ function validateSessionAvatarAsset(input) {
21580
22030
  );
21581
22031
  }
21582
22032
  const projectRoot = realpathSync(input.projectRoot);
21583
- const assetPath = resolve(projectRoot, input.asset);
22033
+ const assetPath = resolve2(projectRoot, input.asset);
21584
22034
  let assetsRoot;
21585
22035
  try {
21586
- assetsRoot = realpathSync(resolve(projectRoot, ".auto", "assets"));
22036
+ assetsRoot = realpathSync(resolve2(projectRoot, ".auto", "assets"));
21587
22037
  } catch {
21588
22038
  throw new Error(
21589
22039
  `Invalid identity avatar asset for "${input.resourceName}": .auto/assets directory does not exist`
@@ -21593,7 +22043,7 @@ function validateSessionAvatarAsset(input) {
21593
22043
  let stat;
21594
22044
  try {
21595
22045
  resolvedAssetPath = realpathSync(assetPath);
21596
- stat = statSync(resolvedAssetPath);
22046
+ stat = statSync2(resolvedAssetPath);
21597
22047
  } catch {
21598
22048
  throw new Error(
21599
22049
  `Invalid identity avatar asset for "${input.resourceName}": ${input.asset} does not exist`
@@ -21614,7 +22064,7 @@ function validateSessionAvatarAsset(input) {
21614
22064
  `Invalid identity avatar asset for "${input.resourceName}": asset must be ${MAX_AVATAR_ASSET_BYTES} bytes or smaller`
21615
22065
  );
21616
22066
  }
21617
- const extension = extname(resolvedAssetPath).toLowerCase();
22067
+ const extension = extname2(resolvedAssetPath).toLowerCase();
21618
22068
  if (!ALLOWED_AVATAR_EXTENSIONS.has(extension)) {
21619
22069
  throw new Error(
21620
22070
  `Invalid identity avatar asset for "${input.resourceName}": asset must be a PNG or JPEG image`
@@ -21623,16 +22073,16 @@ function validateSessionAvatarAsset(input) {
21623
22073
  return resolvedAssetPath;
21624
22074
  }
21625
22075
  function applyProjectRoot(directory) {
21626
- const resolved = resolve(directory);
21627
- return basename2(resolved) === ".auto" ? dirname4(resolved) : resolved;
22076
+ const resolved = resolve2(directory);
22077
+ return basename3(resolved) === ".auto" ? dirname5(resolved) : resolved;
21628
22078
  }
21629
22079
  function applyFileProjectRoot(file2) {
21630
- let dir = dirname4(resolve(file2));
22080
+ let dir = dirname5(resolve2(file2));
21631
22081
  while (true) {
21632
- if (basename2(dir) === ".auto") {
21633
- return dirname4(dir);
22082
+ if (basename3(dir) === ".auto") {
22083
+ return dirname5(dir);
21634
22084
  }
21635
- const parent = dirname4(dir);
22085
+ const parent = dirname5(dir);
21636
22086
  if (parent === dir) {
21637
22087
  return process.cwd();
21638
22088
  }
@@ -21643,7 +22093,7 @@ function isInside(path2, parent) {
21643
22093
  return path2.startsWith(`${parent}/`);
21644
22094
  }
21645
22095
  function applyCandidate(document) {
21646
- if (!isRecord2(document) || !("kind" in document)) {
22096
+ if (!isRecord3(document) || !("kind" in document)) {
21647
22097
  return { kind: RESOURCE_KIND_SESSION, value: document };
21648
22098
  }
21649
22099
  if (document.kind === LEGACY_RESOURCE_KIND_SESSION) {
@@ -21655,9 +22105,11 @@ function applyCandidate(document) {
21655
22105
  }
21656
22106
  };
21657
22107
  }
21658
- if (!PROJECT_RESOURCE_APPLY_ORDER.includes(document.kind)) {
22108
+ if (!PROJECT_APPLY_RESOURCE_KINDS.includes(
22109
+ document.kind
22110
+ )) {
21659
22111
  throw new Error(
21660
- `Unsupported apply resource kind "${String(document.kind)}"; supported kinds are ${PROJECT_RESOURCE_APPLY_ORDER.map((kind2) => `"${kind2}"`).join(", ")} (legacy alias "session" is also accepted)`
22112
+ `Unsupported apply resource kind "${String(document.kind)}"; supported kinds are ${PROJECT_APPLY_RESOURCE_KINDS.map((kind2) => `"${kind2}"`).join(", ")} (legacy alias "session" is also accepted)`
21661
22113
  );
21662
22114
  }
21663
22115
  const kind = document.kind;
@@ -21675,16 +22127,16 @@ function applyDirectories(kind) {
21675
22127
  function primaryApplyDirectory(kind) {
21676
22128
  return APPLY_DIRECTORIES[kind];
21677
22129
  }
21678
- function isRecord2(value) {
22130
+ function isRecord3(value) {
21679
22131
  return typeof value === "object" && value !== null && !Array.isArray(value);
21680
22132
  }
21681
22133
  function resourceApplyFiles(directory, entries) {
21682
22134
  const files = [];
21683
22135
  for (const entry of entries) {
21684
- const path2 = join3(directory, entry.name);
22136
+ const path2 = join4(directory, entry.name);
21685
22137
  if (entry.isDirectory()) {
21686
22138
  files.push(
21687
- ...resourceApplyFiles(path2, readdirSync2(path2, { withFileTypes: true }))
22139
+ ...resourceApplyFiles(path2, readdirSync3(path2, { withFileTypes: true }))
21688
22140
  );
21689
22141
  continue;
21690
22142
  }
@@ -21699,6 +22151,7 @@ var init_files = __esm({
21699
22151
  "src/commands/apply/files.ts"() {
21700
22152
  "use strict";
21701
22153
  init_src();
22154
+ init_authoring();
21702
22155
  APPLY_DIRECTORIES = {
21703
22156
  environment: "environments",
21704
22157
  identity: "identities",
@@ -22003,15 +22456,20 @@ function parseProjectResourceReference(resource) {
22003
22456
  if (!kind || !name || extra) {
22004
22457
  throw new Error('Resource must be formatted as "kind/name"');
22005
22458
  }
22006
- if (!PROJECT_RESOURCE_KINDS.includes(kind)) {
22459
+ if (!PROJECT_APPLY_RESOURCE_KINDS.includes(
22460
+ kind
22461
+ )) {
22007
22462
  if (kind === LEGACY_RESOURCE_KIND_SESSION) {
22008
22463
  return { kind: RESOURCE_KIND_SESSION, name };
22009
22464
  }
22010
22465
  throw new Error(
22011
- `Unsupported resource kind "${kind}"; supported kinds are ${PROJECT_RESOURCE_KINDS.map((value) => `"${value}"`).join(", ")} (legacy alias "session" is also accepted)`
22466
+ `Unsupported resource kind "${kind}"; supported kinds are ${PROJECT_APPLY_RESOURCE_KINDS.map((value) => `"${value}"`).join(", ")} (legacy alias "session" is also accepted)`
22012
22467
  );
22013
22468
  }
22014
- return { kind, name };
22469
+ return {
22470
+ kind,
22471
+ name
22472
+ };
22015
22473
  }
22016
22474
  async function listProjectResources2(client, kind, options) {
22017
22475
  switch (kind) {
@@ -22021,6 +22479,8 @@ async function listProjectResources2(client, kind, options) {
22021
22479
  return (await client.listIdentities(options)).identities;
22022
22480
  case RESOURCE_KIND_SESSION:
22023
22481
  return (await client.listSessions(options)).sessions;
22482
+ default:
22483
+ throw new Error(`Resource kind "${kind}" cannot be listed directly.`);
22024
22484
  }
22025
22485
  }
22026
22486
  async function requireProjectResource(client, reference, options) {
@@ -22083,10 +22543,10 @@ var init_resources3 = __esm({
22083
22543
 
22084
22544
  // src/commands/edit/actions.ts
22085
22545
  import { spawn as spawn2 } from "child_process";
22086
- import { mkdtempSync, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
22546
+ import { mkdtempSync, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
22087
22547
  import { tmpdir } from "os";
22088
- import { join as join4 } from "path";
22089
- import { parseAllDocuments as parseYamlDocuments2, stringify as stringify2 } from "yaml";
22548
+ import { join as join5 } from "path";
22549
+ import { parseAllDocuments as parseYamlDocuments3, stringify as stringify3 } from "yaml";
22090
22550
  async function editResource(input) {
22091
22551
  const reference = parseProjectResourceReference(input.resource);
22092
22552
  const editor = resolveEditor({
@@ -22098,15 +22558,15 @@ async function editResource(input) {
22098
22558
  apiBaseUrl: input.commandOptions.apiBaseUrl
22099
22559
  });
22100
22560
  const document = editableResourceDocument(reference.kind, current);
22101
- const source = `${stringify2(document).trimEnd()}
22561
+ const source = `${stringify3(document).trimEnd()}
22102
22562
  `;
22103
- const tempRoot = mkdtempSync(join4(tmpdir(), "auto-edit-"));
22104
- const filePath = join4(tempRoot, `${reference.kind}-${reference.name}.yaml`);
22563
+ const tempRoot = mkdtempSync(join5(tmpdir(), "auto-edit-"));
22564
+ const filePath = join5(tempRoot, `${reference.kind}-${reference.name}.yaml`);
22105
22565
  writeFileSync3(filePath, source, "utf8");
22106
22566
  let removeTempFile = false;
22107
22567
  try {
22108
22568
  await runEditor(editor, filePath);
22109
- const editedSource = readFileSync4(filePath, "utf8");
22569
+ const editedSource = readFileSync5(filePath, "utf8");
22110
22570
  if (editedSource === source) {
22111
22571
  input.writeOutput("No changes; skipped apply.");
22112
22572
  removeTempFile = true;
@@ -22153,7 +22613,7 @@ function resolveEditor(input) {
22153
22613
  return input.env.VISUAL?.trim() || input.env.EDITOR?.trim() || "vi";
22154
22614
  }
22155
22615
  async function runEditor(editor, filePath) {
22156
- await new Promise((resolve2, reject) => {
22616
+ await new Promise((resolve3, reject) => {
22157
22617
  const child = spawn2(editor, [filePath], {
22158
22618
  shell: true,
22159
22619
  stdio: "inherit"
@@ -22165,7 +22625,7 @@ async function runEditor(editor, filePath) {
22165
22625
  });
22166
22626
  child.on("close", (code, signal) => {
22167
22627
  if (code === 0) {
22168
- resolve2();
22628
+ resolve3();
22169
22629
  return;
22170
22630
  }
22171
22631
  if (signal) {
@@ -22177,10 +22637,10 @@ async function runEditor(editor, filePath) {
22177
22637
  });
22178
22638
  }
22179
22639
  function readEditedResource(filePath, expected) {
22180
- const source = readFileSync4(filePath, "utf8");
22640
+ const source = readFileSync5(filePath, "utf8");
22181
22641
  let documents;
22182
22642
  try {
22183
- documents = parseYamlDocuments2(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
22643
+ documents = parseYamlDocuments3(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
22184
22644
  } catch (error51) {
22185
22645
  throw new Error(
22186
22646
  `Invalid edited resource: ${error51 instanceof Error ? error51.message : String(error51)}`
@@ -24106,13 +24566,13 @@ function sleep2(ms, signal) {
24106
24566
  if (signal.aborted) {
24107
24567
  return Promise.resolve();
24108
24568
  }
24109
- return new Promise((resolve2) => {
24110
- const timeout = setTimeout(resolve2, ms);
24569
+ return new Promise((resolve3) => {
24570
+ const timeout = setTimeout(resolve3, ms);
24111
24571
  signal.addEventListener(
24112
24572
  "abort",
24113
24573
  () => {
24114
24574
  clearTimeout(timeout);
24115
- resolve2();
24575
+ resolve3();
24116
24576
  },
24117
24577
  { once: true }
24118
24578
  );
@@ -25300,20 +25760,25 @@ var init_RunsView = __esm({
25300
25760
  // src/tui/SessionsView.tsx
25301
25761
  import { Box as Box11, Text as Text12, useStdout as useStdout6 } from "ink";
25302
25762
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
25303
- function SessionsView({ sessions, selectedIndex, error: error51 }) {
25763
+ function SessionsView({
25764
+ sessions,
25765
+ selectedIndex,
25766
+ error: error51,
25767
+ authoringStatuses
25768
+ }) {
25304
25769
  const { stdout } = useStdout6();
25305
25770
  const termWidth = stdout?.columns ?? 120;
25306
25771
  const numCols = 4;
25307
25772
  const gapTotal = (numCols - 1) * TABLE_GAP;
25308
25773
  const descriptionWidth = Math.max(
25309
25774
  24,
25310
- termWidth - TABLE_PADDING_X * 2 - COL_NAME2 - COL_RUNS - COL_UPDATED2 - gapTotal
25775
+ termWidth - TABLE_PADDING_X * 2 - COL_NAME2 - COL_AUTHORING - COL_UPDATED2 - gapTotal
25311
25776
  );
25312
25777
  return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", flexGrow: 1, children: [
25313
25778
  /* @__PURE__ */ jsxs10(Box11, { paddingX: TABLE_PADDING_X, paddingBottom: 1, gap: TABLE_GAP, children: [
25314
25779
  /* @__PURE__ */ jsx12(HeaderCell, { width: COL_NAME2, children: "NAME\u2191" }),
25315
- /* @__PURE__ */ jsx12(HeaderCell, { width: descriptionWidth, children: "DESCRIPTION" }),
25316
- /* @__PURE__ */ jsx12(HeaderCell, { width: COL_RUNS, justifyContent: "flex-end", children: "RUNS" }),
25780
+ /* @__PURE__ */ jsx12(HeaderCell, { width: descriptionWidth, children: "CONFIG" }),
25781
+ /* @__PURE__ */ jsx12(HeaderCell, { width: COL_AUTHORING, justifyContent: "flex-end", children: "AUTHORING" }),
25317
25782
  /* @__PURE__ */ jsx12(HeaderCell, { width: COL_UPDATED2, justifyContent: "flex-end", children: "UPDATED" })
25318
25783
  ] }),
25319
25784
  error51 ? /* @__PURE__ */ jsx12(Box11, { paddingX: 2, children: /* @__PURE__ */ jsxs10(Text12, { color: "red", children: [
@@ -25325,7 +25790,9 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
25325
25790
  ] }) }) : sessions.map((session, i) => {
25326
25791
  const isSelected = i === selectedIndex;
25327
25792
  const updated = relativeTime3(session.metadata.updatedAt);
25328
- const runCount = session.status.runCount.toString();
25793
+ const authoring = sessionAuthoringLabel(
25794
+ authoringStatuses?.get(session.metadata.name)
25795
+ );
25329
25796
  return /* @__PURE__ */ jsxs10(TableRow, { selected: isSelected, children: [
25330
25797
  /* @__PURE__ */ jsx12(
25331
25798
  TableCell,
@@ -25344,17 +25811,17 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
25344
25811
  width: descriptionWidth,
25345
25812
  selected: isSelected,
25346
25813
  dim: !isSelected,
25347
- children: "<todo fill in>"
25814
+ children: sessionConfigLabel(session)
25348
25815
  }
25349
25816
  ),
25350
25817
  /* @__PURE__ */ jsx12(
25351
25818
  TableCell,
25352
25819
  {
25353
- width: COL_RUNS,
25820
+ width: COL_AUTHORING,
25354
25821
  selected: isSelected,
25355
25822
  dim: !isSelected,
25356
25823
  justifyContent: "flex-end",
25357
- children: runCount
25824
+ children: authoring
25358
25825
  }
25359
25826
  ),
25360
25827
  /* @__PURE__ */ jsx12(
@@ -25371,6 +25838,26 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
25371
25838
  })
25372
25839
  ] });
25373
25840
  }
25841
+ function sessionConfigLabel(session) {
25842
+ const pieces = [
25843
+ session.spec.harness,
25844
+ session.spec.environment ? `env:${session.spec.environment}` : void 0,
25845
+ `${session.status.runCount} runs`
25846
+ ].filter(Boolean);
25847
+ return pieces.join(" ");
25848
+ }
25849
+ function sessionAuthoringLabel(status) {
25850
+ if (!status) {
25851
+ return "remote";
25852
+ }
25853
+ if (!status.ok) {
25854
+ return "error";
25855
+ }
25856
+ if (status.imports === 0 && status.removals === 0) {
25857
+ return "local ok";
25858
+ }
25859
+ return `ok i${status.imports}/r${status.removals}`;
25860
+ }
25374
25861
  function relativeTime3(dateStr) {
25375
25862
  const ms = Date.now() - new Date(dateStr).getTime();
25376
25863
  const s = Math.floor(ms / 1e3);
@@ -25382,12 +25869,12 @@ function relativeTime3(dateStr) {
25382
25869
  const d = Math.floor(h / 24);
25383
25870
  return `${d}d ago`;
25384
25871
  }
25385
- var COL_RUNS, COL_UPDATED2, COL_NAME2;
25872
+ var COL_AUTHORING, COL_UPDATED2, COL_NAME2;
25386
25873
  var init_SessionsView = __esm({
25387
25874
  "src/tui/SessionsView.tsx"() {
25388
25875
  "use strict";
25389
25876
  init_Table();
25390
- COL_RUNS = 6;
25877
+ COL_AUTHORING = 16;
25391
25878
  COL_UPDATED2 = 10;
25392
25879
  COL_NAME2 = 28;
25393
25880
  }
@@ -25395,7 +25882,7 @@ var init_SessionsView = __esm({
25395
25882
 
25396
25883
  // src/tui/SpecInspector.tsx
25397
25884
  import { Box as Box12, Text as Text13 } from "ink";
25398
- import { stringify as stringify4 } from "yaml";
25885
+ import { stringify as stringify6 } from "yaml";
25399
25886
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
25400
25887
  function SpecInspector({
25401
25888
  kind,
@@ -25426,7 +25913,7 @@ function specLineCount(spec) {
25426
25913
  return yamlLines(spec).length;
25427
25914
  }
25428
25915
  function yamlLines(spec) {
25429
- return stringify4(spec).trimEnd().split("\n");
25916
+ return stringify6(spec).trimEnd().split("\n");
25430
25917
  }
25431
25918
  function YamlLine({ line }) {
25432
25919
  const match = /^(\s*)([^:#][^:]*:)(.*)$/.exec(line);
@@ -25513,6 +26000,15 @@ function selectedNamedResourceForSection(input) {
25513
26000
  return null;
25514
26001
  }
25515
26002
  }
26003
+ function agentAuthoringHeaderLabel(status) {
26004
+ if (!status.ok) {
26005
+ return "error";
26006
+ }
26007
+ if (status.imports === 0 && status.removals === 0) {
26008
+ return "local ok";
26009
+ }
26010
+ return `local ok, ${status.imports} imports, ${status.removals} removals`;
26011
+ }
25516
26012
  function editableResourceForSelection(input) {
25517
26013
  if (!isApplyResourceSection(input.activeSection)) {
25518
26014
  return null;
@@ -25571,6 +26067,15 @@ function HomeView({ apiUrl, notice, returnToSession }) {
25571
26067
  const [serviceAccountNameInput, setServiceAccountNameInput] = useState3("");
25572
26068
  const [inspectedResource, setInspectedResource] = useState3(null);
25573
26069
  const [inspectScrollOffset, setInspectScrollOffset] = useState3(0);
26070
+ const localAgentAuthoringStatuses = useMemo3(
26071
+ () => new Map(
26072
+ readLocalAgentAuthoringStatuses().map((status) => [
26073
+ status.name,
26074
+ status
26075
+ ])
26076
+ ),
26077
+ []
26078
+ );
25574
26079
  const activeSelectionIds = client.getActiveSelection();
25575
26080
  const {
25576
26081
  data: sessions = [],
@@ -25673,6 +26178,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
25673
26178
  const selectedConnectionProvider = providerRows2(connectionProviders)[connectionProviderIndex];
25674
26179
  const selectedServiceAccount = serviceAccounts[serviceAccountIndex];
25675
26180
  const selectedSession = sessions[sessionIndex];
26181
+ const selectedAgentAuthoringStatus = selectedSession ? localAgentAuthoringStatuses.get(selectedSession.metadata.name) : void 0;
25676
26182
  const selectedEnvironment = environments[environmentIndex];
25677
26183
  const selectedIdentity = identities[identityIndex];
25678
26184
  const selectedInspectableResource = useMemo3(() => {
@@ -26620,6 +27126,14 @@ function HomeView({ apiUrl, notice, returnToSession }) {
26620
27126
  activeProject && /* @__PURE__ */ jsx14(KV, { label: "Project", value: activeProject.projectName }),
26621
27127
  activeProject && /* @__PURE__ */ jsx14(KV, { label: "Role", value: activeProject.role }),
26622
27128
  selectedSession && /* @__PURE__ */ jsx14(KV, { label: "Agent", value: selectedSession.metadata.name }),
27129
+ selectedAgentAuthoringStatus && /* @__PURE__ */ jsx14(
27130
+ KV,
27131
+ {
27132
+ label: "Authoring",
27133
+ value: agentAuthoringHeaderLabel(selectedAgentAuthoringStatus),
27134
+ color: selectedAgentAuthoringStatus.ok ? void 0 : "red"
27135
+ }
27136
+ ),
26623
27137
  runError && /* @__PURE__ */ jsx14(KV, { label: "Error", value: runError, color: "red" }),
26624
27138
  isRunMutationPending && /* @__PURE__ */ jsx14(
26625
27139
  KV,
@@ -26768,7 +27282,8 @@ function HomeView({ apiUrl, notice, returnToSession }) {
26768
27282
  {
26769
27283
  sessions,
26770
27284
  selectedIndex: sessionIndex,
26771
- error: sessionsErrorMessage
27285
+ error: sessionsErrorMessage,
27286
+ authoringStatuses: localAgentAuthoringStatuses
26772
27287
  }
26773
27288
  ) : 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: [
26774
27289
  serviceAccountInputMode === "create" && /* @__PURE__ */ jsxs12(Box13, { paddingX: 2, gap: 1, children: [
@@ -27087,6 +27602,7 @@ var init_HomeView = __esm({
27087
27602
  "src/tui/HomeView.tsx"() {
27088
27603
  "use strict";
27089
27604
  init_src();
27605
+ init_authoring();
27090
27606
  init_login();
27091
27607
  init_base_url();
27092
27608
  init_browser();
@@ -27339,6 +27855,9 @@ async function launch(opts) {
27339
27855
  }
27340
27856
  }
27341
27857
  async function refreshEditedResource(input) {
27858
+ if (!isRefreshableResourceKind(input.kind)) {
27859
+ return;
27860
+ }
27342
27861
  const definition = RESOURCE_REFRESH_DEFINITIONS[input.kind];
27343
27862
  input.queryClient.setQueryData(
27344
27863
  definition.queryKey(input.apiUrl),
@@ -27353,6 +27872,9 @@ function sortByResourceName(resources) {
27353
27872
  function isEditSelection(selection) {
27354
27873
  return "action" in selection && selection.action === "editResource";
27355
27874
  }
27875
+ function isRefreshableResourceKind(kind) {
27876
+ return kind in RESOURCE_REFRESH_DEFINITIONS;
27877
+ }
27356
27878
  var RESOURCE_REFRESH_DEFINITIONS;
27357
27879
  var init_launcher = __esm({
27358
27880
  "src/tui/launcher.ts"() {
@@ -27794,8 +28316,8 @@ async function startGitCredentialRelay(input) {
27794
28316
  update: (next) => {
27795
28317
  target = { url: next.url, accessToken: next.accessToken };
27796
28318
  },
27797
- close: () => new Promise((resolve2) => {
27798
- server.close(() => resolve2());
28319
+ close: () => new Promise((resolve3) => {
28320
+ server.close(() => resolve3());
27799
28321
  })
27800
28322
  };
27801
28323
  }
@@ -28218,12 +28740,12 @@ async function runAgentBridgeSocket(options) {
28218
28740
  let handler = null;
28219
28741
  const reconnectLoop = createReconnectLoop(socket);
28220
28742
  let hasConnected = false;
28221
- await new Promise((resolve2, reject) => {
28743
+ await new Promise((resolve3, reject) => {
28222
28744
  const shutdown = () => {
28223
28745
  reconnectLoop.stop();
28224
28746
  handler?.shutdown?.();
28225
28747
  socket.disconnect();
28226
- resolve2();
28748
+ resolve3();
28227
28749
  };
28228
28750
  process.once("SIGINT", shutdown);
28229
28751
  process.once("SIGTERM", shutdown);
@@ -28406,7 +28928,7 @@ function createRuntimeBridgeBootstrapListener(input) {
28406
28928
  };
28407
28929
  }
28408
28930
  function emitOutputWithAck(socket, output) {
28409
- return new Promise((resolve2, reject) => {
28931
+ return new Promise((resolve3, reject) => {
28410
28932
  socket.timeout(5e3).emit(
28411
28933
  RUNTIME_BRIDGE_OUTPUT_EVENT,
28412
28934
  output,
@@ -28426,7 +28948,7 @@ function emitOutputWithAck(socket, output) {
28426
28948
  );
28427
28949
  return;
28428
28950
  }
28429
- resolve2(ack.data);
28951
+ resolve3(ack.data);
28430
28952
  }
28431
28953
  );
28432
28954
  });
@@ -29169,8 +29691,8 @@ var AsyncMessageQueue = class {
29169
29691
  if (this.closed) {
29170
29692
  return Promise.resolve({ done: true, value: void 0 });
29171
29693
  }
29172
- return new Promise((resolve2) => {
29173
- this.waiters.push(resolve2);
29694
+ return new Promise((resolve3) => {
29695
+ this.waiters.push(resolve3);
29174
29696
  });
29175
29697
  }
29176
29698
  };
@@ -29434,13 +29956,13 @@ var ClaudeCodeCommandHandler = class {
29434
29956
  this.input.writeOutput?.(
29435
29957
  `agent_bridge_question_pending tool_use_id=${toolUseId}`
29436
29958
  );
29437
- return new Promise((resolve2) => {
29438
- this.pendingQuestions.set(toolUseId, { input: toolInput, resolve: resolve2 });
29959
+ return new Promise((resolve3) => {
29960
+ this.pendingQuestions.set(toolUseId, { input: toolInput, resolve: resolve3 });
29439
29961
  options.signal.addEventListener(
29440
29962
  "abort",
29441
29963
  () => {
29442
29964
  if (this.pendingQuestions.delete(toolUseId)) {
29443
- resolve2({
29965
+ resolve3({
29444
29966
  behavior: "deny",
29445
29967
  message: "The question was cancelled before the user answered",
29446
29968
  toolUseID: toolUseId
@@ -30089,7 +30611,7 @@ async function activeGrantIds(input) {
30089
30611
  );
30090
30612
  }
30091
30613
  async function waitForNewGrant(input, knownGrantIds) {
30092
- const sleep3 = input.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
30614
+ const sleep3 = input.sleep ?? ((ms) => new Promise((resolve3) => setTimeout(resolve3, ms)));
30093
30615
  const now3 = input.now ?? Date.now;
30094
30616
  const deadline = now3() + (input.pollTimeoutMs ?? POLL_TIMEOUT_MS);
30095
30617
  while (now3() < deadline) {
@@ -30786,13 +31308,13 @@ function registerDeleteCommands(program, context) {
30786
31308
 
30787
31309
  // src/commands/describe/actions.ts
30788
31310
  init_resources3();
30789
- import { stringify } from "yaml";
31311
+ import { stringify as stringify2 } from "yaml";
30790
31312
  async function inspectResource(input) {
30791
31313
  const request = parseProjectResourceReference(input.resource);
30792
31314
  const resource = await requireProjectResource(input.client, request, {
30793
31315
  apiBaseUrl: input.commandOptions.apiBaseUrl
30794
31316
  });
30795
- input.writeOutput(stringify(resource.spec).trimEnd());
31317
+ input.writeOutput(stringify2(resource.spec).trimEnd());
30796
31318
  }
30797
31319
 
30798
31320
  // src/commands/describe/commands.ts
@@ -31469,13 +31991,13 @@ function delay(ms, signal) {
31469
31991
  if (signal.aborted) {
31470
31992
  return Promise.resolve();
31471
31993
  }
31472
- return new Promise((resolve2) => {
31473
- const timeout = setTimeout(resolve2, ms);
31994
+ return new Promise((resolve3) => {
31995
+ const timeout = setTimeout(resolve3, ms);
31474
31996
  signal.addEventListener(
31475
31997
  "abort",
31476
31998
  () => {
31477
31999
  clearTimeout(timeout);
31478
- resolve2();
32000
+ resolve3();
31479
32001
  },
31480
32002
  { once: true }
31481
32003
  );
@@ -31500,11 +32022,11 @@ function pollUntilFailed(input) {
31500
32022
  input.abort?.addEventListener("abort", cancel, { once: true });
31501
32023
  const done = (async () => {
31502
32024
  while (!cancelled) {
31503
- await new Promise((resolve2) => {
31504
- sleepResolve = resolve2;
32025
+ await new Promise((resolve3) => {
32026
+ sleepResolve = resolve3;
31505
32027
  activeSleep = setTimeout(() => {
31506
32028
  activeSleep = void 0;
31507
- resolve2();
32029
+ resolve3();
31508
32030
  }, interval);
31509
32031
  });
31510
32032
  if (cancelled) {
@@ -31841,7 +32363,7 @@ function delay2(ms) {
31841
32363
  if (ms <= 0) {
31842
32364
  return Promise.resolve();
31843
32365
  }
31844
- return new Promise((resolve2) => setTimeout(resolve2, ms));
32366
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
31845
32367
  }
31846
32368
  function createDiagnosticCollector(limit) {
31847
32369
  const events = [];
@@ -32239,9 +32761,9 @@ function entryPreview(event, full) {
32239
32761
  case "text":
32240
32762
  return part.text;
32241
32763
  case "tool_call":
32242
- return `${part.name} ${stringify3(part.input)}`;
32764
+ return `${part.name} ${stringify4(part.input)}`;
32243
32765
  case "tool_result":
32244
- return stringify3(part.output);
32766
+ return stringify4(part.output);
32245
32767
  case "question":
32246
32768
  return part.questions.map((question) => question.question).join(" ");
32247
32769
  default:
@@ -32252,9 +32774,9 @@ function entryPreview(event, full) {
32252
32774
  return full ? joined : clip(joined);
32253
32775
  }
32254
32776
  function preview(value) {
32255
- return clip(singleLine(stringify3(value)));
32777
+ return clip(singleLine(stringify4(value)));
32256
32778
  }
32257
- function stringify3(value) {
32779
+ function stringify4(value) {
32258
32780
  return typeof value === "string" ? value : JSON.stringify(value);
32259
32781
  }
32260
32782
  function singleLine(text) {
@@ -32699,12 +33221,57 @@ function withApiBaseUrl2(context, commandOptions) {
32699
33221
  };
32700
33222
  }
32701
33223
 
33224
+ // src/commands/agents/actions.ts
33225
+ init_authoring();
33226
+ import { stringify as stringify5 } from "yaml";
33227
+ function renderAgent(input) {
33228
+ const result = compileAgent(input);
33229
+ input.writeOutput(renderCompiledAgentYaml(result));
33230
+ }
33231
+ function compileAgent(input) {
33232
+ const path2 = resolveAgentAuthoringPath({
33233
+ agent: input.agent,
33234
+ directory: input.options.directory
33235
+ });
33236
+ return compileAgentFile(path2);
33237
+ }
33238
+ function compileAgentCommand(input) {
33239
+ const result = compileAgent(input);
33240
+ input.writeOutput(
33241
+ input.options.json ? JSON.stringify(result.resource) : stringify5(result.resource).trimEnd()
33242
+ );
33243
+ }
33244
+ function explainAgent(input) {
33245
+ const result = compileAgent(input);
33246
+ input.writeOutput(
33247
+ input.options.json ? JSON.stringify(result) : renderAgentExplain(result)
33248
+ );
33249
+ }
33250
+ function statusAgents(input) {
33251
+ const statuses = readLocalAgentAuthoringStatuses({
33252
+ directory: input.options.directory
33253
+ });
33254
+ if (input.options.json) {
33255
+ input.writeOutput(JSON.stringify(statuses));
33256
+ return;
33257
+ }
33258
+ if (statuses.length === 0) {
33259
+ input.writeOutput("No local agent authoring files found.");
33260
+ return;
33261
+ }
33262
+ for (const status of statuses) {
33263
+ const state = status.ok ? "ok" : "error";
33264
+ const suffix = status.ok ? `imports=${status.imports} removals=${status.removals}` : status.error;
33265
+ input.writeOutput(`${state} ${status.name} ${suffix}`);
33266
+ }
33267
+ }
33268
+
32702
33269
  // src/commands/sessions/connect.ts
32703
33270
  init_resources2();
32704
33271
  init_browser();
32705
33272
  import { existsSync as existsSync3, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync4 } from "fs";
32706
33273
  import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
32707
- import { join as join5 } from "path";
33274
+ import { join as join6 } from "path";
32708
33275
  var POLL_INTERVAL_MS2 = 2e3;
32709
33276
  var POLL_TIMEOUT_MS2 = 5 * 6e4;
32710
33277
  var SLACK_APPS_URL = "https://api.slack.com/apps";
@@ -32756,7 +33323,7 @@ async function connectSessionPresence2(input) {
32756
33323
  );
32757
33324
  return;
32758
33325
  }
32759
- const sleep3 = input.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
33326
+ const sleep3 = input.sleep ?? ((ms) => new Promise((resolve3) => setTimeout(resolve3, ms)));
32760
33327
  const openBrowser2 = input.openBrowser ?? openBrowser;
32761
33328
  const now3 = input.now ?? Date.now;
32762
33329
  const pollIntervalMs = input.pollIntervalMs ?? POLL_INTERVAL_MS2;
@@ -32875,7 +33442,7 @@ async function promptForIconUploads(input, options) {
32875
33442
  }
32876
33443
  }
32877
33444
  function stagedLocationLabel(stagedPath) {
32878
- return stagedPath.startsWith(`${join5(homedir3(), "Downloads")}/`) ? "Downloads" : "the printed path";
33445
+ return stagedPath.startsWith(`${join6(homedir3(), "Downloads")}/`) ? "Downloads" : "the printed path";
32879
33446
  }
32880
33447
  async function stageAvatarImage(input) {
32881
33448
  try {
@@ -32887,9 +33454,9 @@ async function stageAvatarImage(input) {
32887
33454
  }
32888
33455
  const contentType = response.headers.get("content-type") ?? "";
32889
33456
  const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
32890
- const downloads = join5(homedir3(), "Downloads");
32891
- const directory = existsSync3(downloads) ? downloads : mkdtempSync2(join5(tmpdir2(), "auto-avatar-"));
32892
- const path2 = join5(directory, `${input.session}-avatar${extension}`);
33457
+ const downloads = join6(homedir3(), "Downloads");
33458
+ const directory = existsSync3(downloads) ? downloads : mkdtempSync2(join6(tmpdir2(), "auto-avatar-"));
33459
+ const path2 = join6(directory, `${input.session}-avatar${extension}`);
32893
33460
  writeFileSync4(path2, Buffer.from(await response.arrayBuffer()));
32894
33461
  return path2;
32895
33462
  } catch {
@@ -32985,6 +33552,45 @@ function registerSessionCommands(program, context) {
32985
33552
  });
32986
33553
  });
32987
33554
  const sessions = program.command("agents").alias("sessions").description("Manage Agent resources.");
33555
+ sessions.command("render").description("Render a local Agent authoring file after imports/removals.").argument("<agent>", "agent name or path").option(
33556
+ "--directory <directory>",
33557
+ "directory containing Auto resource files"
33558
+ ).action((agent, commandOptions) => {
33559
+ renderAgent({
33560
+ agent,
33561
+ options: commandOptions,
33562
+ writeOutput: context.writeOutput
33563
+ });
33564
+ });
33565
+ sessions.command("compile").description("Compile a local Agent authoring file into an apply resource.").argument("<agent>", "agent name or path").option(
33566
+ "--directory <directory>",
33567
+ "directory containing Auto resource files"
33568
+ ).option("--json", "print the compiled resource as JSON").action((agent, commandOptions) => {
33569
+ compileAgentCommand({
33570
+ agent,
33571
+ options: commandOptions,
33572
+ writeOutput: context.writeOutput
33573
+ });
33574
+ });
33575
+ sessions.command("explain").description("Explain a local Agent authoring file's import graph.").argument("<agent>", "agent name or path").option(
33576
+ "--directory <directory>",
33577
+ "directory containing Auto resource files"
33578
+ ).option("--json", "print the import graph as JSON").action((agent, commandOptions) => {
33579
+ explainAgent({
33580
+ agent,
33581
+ options: commandOptions,
33582
+ writeOutput: context.writeOutput
33583
+ });
33584
+ });
33585
+ sessions.command("status").description("Validate local Agent authoring files.").option(
33586
+ "--directory <directory>",
33587
+ "directory containing Auto resource files"
33588
+ ).option("--json", "print local authoring status as JSON").action((commandOptions) => {
33589
+ statusAgents({
33590
+ options: commandOptions,
33591
+ writeOutput: context.writeOutput
33592
+ });
33593
+ });
32988
33594
  sessions.command("connect").description(
32989
33595
  "Connect an agent's provider presence (e.g. install its Slack agent app)."
32990
33596
  ).argument("<session>", "agent resource name").option("--manual", "print authorization URLs without opening a browser").option(