@hasna/instructions 0.7.1 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +47 -7
  2. package/dist/chunks/{apply-c3sd4agz.js → apply-1gyr8r0x.js} +3 -3
  3. package/dist/chunks/{apply-pnn7a3y7.js → apply-fyfzqc7s.js} +3 -3
  4. package/dist/chunks/{index-3djthcm6.js → index-27az1qb1.js} +28 -1
  5. package/dist/chunks/{index-r7nfef2g.js → index-6sbmg1vc.js} +3 -3
  6. package/dist/chunks/{index-pdpg8a44.js → index-axs3yftz.js} +2 -2
  7. package/dist/chunks/{index-aj66f6xr.js → index-dwagz53p.js} +92 -8
  8. package/dist/chunks/{index-6jncnnw7.js → index-enh4yv85.js} +274 -31
  9. package/dist/chunks/{index-ypcef3a2.js → index-n9arv47w.js} +2 -2
  10. package/dist/chunks/{index-n3fjspr0.js → index-preyvj8n.js} +28 -1
  11. package/dist/chunks/{index-jg8ptcwh.js → index-z27xp7fh.js} +2 -2
  12. package/dist/chunks/{local-0a9gja58.js → local-5dsm856v.js} +15 -4
  13. package/dist/chunks/{local-969ye538.js → local-yxsbs8x3.js} +15 -4
  14. package/dist/chunks/{sync-xtkbm5br.js → sync-6r1v8hd3.js} +4 -4
  15. package/dist/chunks/{sync-q5hvwq6e.js → sync-k3pab4s6.js} +4 -4
  16. package/dist/chunks/{template-wgnzdn7h.js → template-e4qrxs8p.js} +1 -1
  17. package/dist/chunks/{template-fsfxpe4p.js → template-nhn124zd.js} +1 -1
  18. package/dist/cli/index.js +1018 -584
  19. package/dist/data/config-store.d.ts +12 -4
  20. package/dist/data/config-store.d.ts.map +1 -1
  21. package/dist/db/configs.d.ts.map +1 -1
  22. package/dist/index.d.ts +8 -4
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1264 -601
  25. package/dist/lib/asset-plan.d.ts +4 -1
  26. package/dist/lib/asset-plan.d.ts.map +1 -1
  27. package/dist/lib/codewith-shared-todos-storage-standard.d.ts.map +1 -1
  28. package/dist/lib/dangerous-operation-guard-standard.d.ts.map +1 -1
  29. package/dist/lib/global-agent-rules-standard.d.ts.map +1 -1
  30. package/dist/lib/global-source-coverage.d.ts +12 -7
  31. package/dist/lib/global-source-coverage.d.ts.map +1 -1
  32. package/dist/lib/harness-discovery.d.ts +52 -0
  33. package/dist/lib/harness-discovery.d.ts.map +1 -0
  34. package/dist/lib/instruction-graph.d.ts +1 -1
  35. package/dist/lib/instruction-graph.d.ts.map +1 -1
  36. package/dist/lib/instruction-source-policy.d.ts +6 -0
  37. package/dist/lib/instruction-source-policy.d.ts.map +1 -0
  38. package/dist/lib/machine.d.ts.map +1 -1
  39. package/dist/lib/managed-skill-runtimes.d.ts +12 -28
  40. package/dist/lib/managed-skill-runtimes.d.ts.map +1 -1
  41. package/dist/lib/platform-profiles.d.ts.map +1 -1
  42. package/dist/lib/project-dashboard-standard.d.ts.map +1 -1
  43. package/dist/lib/session-apply.d.ts +35 -0
  44. package/dist/lib/session-apply.d.ts.map +1 -1
  45. package/dist/lib/session-refresh.d.ts +30 -0
  46. package/dist/lib/session-refresh.d.ts.map +1 -0
  47. package/dist/lib/session-render.d.ts +41 -1
  48. package/dist/lib/session-render.d.ts.map +1 -1
  49. package/dist/lib/transforms.d.ts +1 -0
  50. package/dist/lib/transforms.d.ts.map +1 -1
  51. package/dist/mcp/index.js +13 -12
  52. package/dist/mcp/server.d.ts.map +1 -1
  53. package/dist/sdk/index.d.ts +2 -0
  54. package/dist/sdk/index.d.ts.map +1 -1
  55. package/dist/sdk/index.js +172 -0
  56. package/dist/sdk/v1-client.d.ts +7 -1
  57. package/dist/sdk/v1-client.d.ts.map +1 -1
  58. package/dist/sdk/v1.generated.d.ts +26 -0
  59. package/dist/sdk/v1.generated.d.ts.map +1 -1
  60. package/dist/server/index.js +199 -8
  61. package/dist/server/openapi.d.ts +150 -0
  62. package/dist/server/openapi.d.ts.map +1 -1
  63. package/dist/server/v1.d.ts.map +1 -1
  64. package/dist/storage/cloud-store.d.ts.map +1 -1
  65. package/dist/types/index.d.ts +24 -2
  66. package/dist/types/index.d.ts.map +1 -1
  67. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16,7 +16,12 @@ var __export = (target, all) => {
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
18
  // src/types/index.ts
19
- var CONFIG_KINDS, CONFIG_CATEGORIES, CONFIG_AGENTS, CONFIG_TRANSFORMS, CONFIG_FORMATS, PROFILE_CONFIG_BINDING_SCHEMA = "hasna.instructions.profile-config-binding/v1", INSTRUCTION_ACTIVATION_MODES, INSTRUCTION_FALLBACKS, PROFILE_ASSET_BINDING_SCHEMA = "hasna.instructions.profile-asset-binding/v1", ASSET_KINDS, ASSET_DESTINATION_STRATEGIES, ASSET_SCOPES, ASSET_UNINSTALL_POLICIES, ASSET_ROLLBACK_POLICIES, INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA = "hasna.instructions.domain-archive/v2", ConfigNotFoundError, ProfileNotFoundError, ConfigApplyError, TemplateRenderError;
19
+ function validateExpectedConfigVersion(value) {
20
+ if (value !== undefined && (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)) {
21
+ throw new InvalidExpectedVersionError;
22
+ }
23
+ }
24
+ var CONFIG_KINDS, CONFIG_CATEGORIES, CONFIG_AGENTS, CONFIG_TRANSFORMS, CONFIG_FORMATS, PROFILE_CONFIG_BINDING_SCHEMA = "hasna.instructions.profile-config-binding/v1", INSTRUCTION_ACTIVATION_MODES, INSTRUCTION_FALLBACKS, PROFILE_ASSET_BINDING_SCHEMA = "hasna.instructions.profile-asset-binding/v1", ASSET_KINDS, ASSET_DESTINATION_STRATEGIES, ASSET_SCOPES, ASSET_UNINSTALL_POLICIES, ASSET_ROLLBACK_POLICIES, INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA = "hasna.instructions.domain-archive/v2", InvalidExpectedVersionError, ConfigVersionConflictError, ConfigNotFoundError, ProfileNotFoundError, ConfigApplyError, TemplateRenderError;
20
25
  var init_types = __esm(() => {
21
26
  CONFIG_KINDS = ["file", "reference"];
22
27
  CONFIG_CATEGORIES = [
@@ -33,6 +38,7 @@ var init_types = __esm(() => {
33
38
  "claude",
34
39
  "codex",
35
40
  "opencode",
41
+ "sumi",
36
42
  "cursor",
37
43
  "codewith",
38
44
  "aicopilot",
@@ -53,6 +59,7 @@ var init_types = __esm(() => {
53
59
  "claude-passthrough",
54
60
  "codex-flat",
55
61
  "opencode-flat",
62
+ "sumi-flat",
56
63
  "cursor-mdc",
57
64
  "skill-neutral"
58
65
  ];
@@ -71,6 +78,25 @@ var init_types = __esm(() => {
71
78
  ASSET_SCOPES = ["global", "project", "session"];
72
79
  ASSET_UNINSTALL_POLICIES = ["remove-managed", "retain"];
73
80
  ASSET_ROLLBACK_POLICIES = ["snapshot", "installer-receipt", "none"];
81
+ InvalidExpectedVersionError = class InvalidExpectedVersionError extends Error {
82
+ code = "INVALID_EXPECTED_VERSION";
83
+ constructor() {
84
+ super("expected_version must be a positive safe integer");
85
+ this.name = "InvalidExpectedVersionError";
86
+ }
87
+ };
88
+ ConfigVersionConflictError = class ConfigVersionConflictError extends Error {
89
+ config_id;
90
+ expected_version;
91
+ code = "CONFIG_VERSION_CONFLICT";
92
+ status = 409;
93
+ constructor(config_id, expected_version) {
94
+ super(`Config version conflict: ${config_id} no longer has expected version ${expected_version}`);
95
+ this.config_id = config_id;
96
+ this.expected_version = expected_version;
97
+ this.name = "ConfigVersionConflictError";
98
+ }
99
+ };
74
100
  ConfigNotFoundError = class ConfigNotFoundError extends Error {
75
101
  constructor(id) {
76
102
  super(`Config not found: ${id}`);
@@ -143,6 +169,31 @@ function boundedReadPage(items, total, options = {}) {
143
169
  }
144
170
  var init_bounded_read = () => {};
145
171
 
172
+ // src/lib/instruction-source-policy.ts
173
+ function isRetiredInstructionSource(config) {
174
+ return config.tags.some((tag) => tag === "retired-global-source" || tag === "retired-instruction-source");
175
+ }
176
+ function instructionSourceRejection(config) {
177
+ if (isRetiredInstructionSource(config) || config.tags.includes("config-only")) {
178
+ return "source is retired or explicitly configuration-only";
179
+ }
180
+ if (config.category !== "rules")
181
+ return `category ${config.category} is not an instruction source; classify reviewed prose as rules`;
182
+ if (config.format !== "markdown" && config.format !== "text")
183
+ return `format ${config.format} is not instruction prose`;
184
+ if (config.is_template)
185
+ return "unresolved templates must be rendered and reviewed before prompt injection";
186
+ if (!config.content.trim())
187
+ return "instruction source is empty";
188
+ if (config.content.includes("\x00"))
189
+ return "instruction source contains binary content";
190
+ const target = config.target_path?.split(/[\\/]/).pop() ?? "";
191
+ if (/\.(?:json|toml|yaml|yml|ini|sh|bash|zsh|fish|js|ts|py|rules)$/i.test(target)) {
192
+ return "target is an executable, provider policy, or settings file rather than a prompt";
193
+ }
194
+ return null;
195
+ }
196
+
146
197
  // src/lib/global-agent-rules-standard.ts
147
198
  import { createHash } from "crypto";
148
199
  function parseAgentOperatingRulesVersion(content) {
@@ -246,9 +297,13 @@ async function ensureGlobalAgentRulesStandardConfig(store = resolveConfigStore()
246
297
  let existing;
247
298
  try {
248
299
  existing = await store.getConfig(GLOBAL_AGENT_RULES_STANDARD_SLUG);
249
- } catch {
300
+ } catch (error) {
301
+ if (!(error instanceof ConfigNotFoundError))
302
+ throw error;
250
303
  return await store.createConfig(standardConfigInput(resolveAgentOperatingRulesPayload(null)));
251
304
  }
305
+ if (isRetiredInstructionSource(existing))
306
+ return existing;
252
307
  const payload = resolveAgentOperatingRulesPayload(existing.content);
253
308
  const input = standardConfigInput(payload);
254
309
  if (existing.content !== input.content || existing.description !== input.description || existing.category !== input.category || existing.agent !== input.agent || existing.format !== input.format || existing.kind !== input.kind || JSON.stringify(existing.tags) !== JSON.stringify(input.tags)) {
@@ -258,6 +313,7 @@ async function ensureGlobalAgentRulesStandardConfig(store = resolveConfigStore()
258
313
  }
259
314
  var GLOBAL_AGENT_RULES_STANDARD_SLUG = "global-agent-rules-standard", AGENT_OPERATING_RULES_SOURCE_SET_ID = "hasna-global-agent-rules-standard", AGENT_OPERATING_RULES_SOURCE_ID = "hasna-agent-operating-rules", AGENT_OPERATING_RULES_ROLE = "agent-operating-rules", AGENT_OPERATING_RULES_VERSION = "1.1.26", AGENT_OPERATING_RULES_SOURCE_SET_VERSION = "2026-08-11", AGENT_OPERATING_RULES_SEMANTIC_POLICY_KEY = "hasna:agent-operating-rules", AGENT_OPERATING_RULES_SENTINEL_PATTERN, AGENT_OPERATING_RULES_HEADING_PATTERN, AGENT_OPERATING_RULES_PAYLOAD_SHA256 = "486844a3d869e3dabc2f33fd66479d4e2fb0a0d1864a3d8a3dd05669eb3f1b77", AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256 = "b8e89cdb49e207e5b497ac51384d67022b94fe5645cc9273db60384eb2c2fb32", SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE = "hasna-agent-operating-rules/scoped-operational-control/v1", AGENT_OPERATING_RULES_UPSTREAM, SCOPED_OPERATIONAL_CONTROL_POLICY, AGENT_OPERATING_RULES_PROVENANCE, AGENT_OPERATING_RULES_METADATA, GLOBAL_AGENT_RULES_STANDARD_CONTENT;
260
315
  var init_global_agent_rules_standard = __esm(() => {
316
+ init_types();
261
317
  init_config_store();
262
318
  AGENT_OPERATING_RULES_SENTINEL_PATTERN = /<!--\s*hasna:agent-operating-rules\s+v=([0-9]+\.[0-9]+\.[0-9]+)\s*-->/i;
263
319
  AGENT_OPERATING_RULES_HEADING_PATTERN = /^#\s*Hasna Agent Operating Rules\s+\u2014\s+v[0-9]+\.[0-9]+\.[0-9]+\s+\(([0-9]{4}-[0-9]{2}-[0-9]{2})\)/;
@@ -384,21 +440,23 @@ async function ensureCodewithSharedTodosStorageStandardConfig(store = resolveCon
384
440
  let config;
385
441
  try {
386
442
  const existing = await store.getConfig(CODEWITH_SHARED_TODOS_STORAGE_STANDARD_SLUG);
443
+ if (isRetiredInstructionSource(existing))
444
+ return existing;
387
445
  if (existing.content !== input.content || existing.description !== input.description || existing.category !== input.category || existing.agent !== input.agent || existing.format !== input.format || existing.kind !== input.kind || JSON.stringify(existing.tags) !== JSON.stringify(input.tags)) {
388
446
  config = await store.updateConfig(existing.id, input);
389
447
  } else {
390
448
  config = existing;
391
449
  }
392
- } catch {
450
+ } catch (error) {
451
+ if (!(error instanceof ConfigNotFoundError))
452
+ throw error;
393
453
  config = await store.createConfig(input);
394
454
  }
395
- for (const profile of await store.listProfiles()) {
396
- await store.addConfigToProfile(profile.id, config.id);
397
- }
398
455
  return config;
399
456
  }
400
457
  var CODEWITH_SHARED_TODOS_STORAGE_STANDARD_SLUG = "codewith-shared-todos-storage-standard", CODEWITH_SHARED_TODOS_STORAGE_POLICY_REFERENCE = "codewith/shared-todos-storage/v1", CODEWITH_SHARED_TODOS_STORAGE_STANDARD_CONTENT;
401
458
  var init_codewith_shared_todos_storage_standard = __esm(() => {
459
+ init_types();
402
460
  init_config_store();
403
461
  CODEWITH_SHARED_TODOS_STORAGE_STANDARD_CONTENT = `# Codewith Shared Todos Storage Standard
404
462
 
@@ -7292,7 +7350,7 @@ function sha2562(content) {
7292
7350
  function isRecord(value) {
7293
7351
  return !!value && typeof value === "object" && !Array.isArray(value);
7294
7352
  }
7295
- var PROJECT_CONTEXT_SCHEMA = "hasna.projects.project_context_bundle.v1", PROJECT_CONTEXT_SCHEMA_V2 = "hasna.projects.project_context_bundle.v2", PROJECT_CONTEXT_SUPPORTED_SCHEMAS, projectContextSchema, PROJECT_CONTEXT_MAX_INPUT_BYTES, PROJECT_CONTEXT_MAX_RENDERED_BYTES, PROJECT_CONTEXT_MAX_APPROX_TOKENS = 1000, PROJECT_CONTEXT_MAX_COMMANDS = 6, PROJECT_CONTEXT_MAX_WARNINGS = 3, PROJECT_CONTEXT_FRAGMENT_PATH = ".hasna/instructions/project-context.md", PROJECT_CONTEXT_MANIFEST_PATH = ".hasna/projects/project-context-manifest.json", PROJECT_CONTEXT_CACHE_PATH = ".hasna/projects/project-context-cache.json", PROJECT_CONTEXT_LOCK_PATH = ".hasna/projects/project-context.lock", PROJECT_CONTEXT_SNAPSHOT_DIR = ".hasna/projects/project-context-snapshots", PROJECT_CONTEXT_CACHE_SCHEMA = "hasna.instructions.project-context-cache/v1", PROJECT_CONTEXT_MANAGED_COMMENT = "Managed by @hasna/configs project context", SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES, FOREIGN_INPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_PATHS, SESSION_MANAGED_OUTPUT_WARN_BYTES, PROJECT_CONTEXT_LOCK_STALE_MS, LEGACY_CONFIGS_PACKAGE = "@hasna/configs", LEGACY_CONFIGS_COMPAT_VERSION = "0.2.45", LEGACY_CONFIGS_EXECUTABLE = "configs", PROJECT_KINDS, PROJECT_STATUSES, LINK_STATES, RESOLUTION_SOURCES, safeId, nullableId, producerSlug, producerName, safeOptionalDisplay, isoTimestamp, revisionSchema, hashSchema, absolutePath, commandArg, financeText, financeLegalEntity, financeProjectMetadataSchema, commandSchema, projectContextBundleSchema, storedManifestProjectContextSchema, storedManifestFileSchema, storedManifestObservationSchema, projectContextMetadataSnapshotSchema, projectContextCacheSchema, ProjectContextError, ProjectContextHashRace, anchoredFsOps, atomicExchange, atomicExchangeLibraries;
7353
+ var PROJECT_CONTEXT_SCHEMA = "hasna.projects.project_context_bundle.v1", PROJECT_CONTEXT_SCHEMA_V2 = "hasna.projects.project_context_bundle.v2", PROJECT_CONTEXT_SUPPORTED_SCHEMAS, projectContextSchema, PROJECT_CONTEXT_MAX_INPUT_BYTES, PROJECT_CONTEXT_MAX_RENDERED_BYTES, PROJECT_CONTEXT_MAX_APPROX_TOKENS = 1000, PROJECT_CONTEXT_MAX_COMMANDS = 6, PROJECT_CONTEXT_MAX_WARNINGS = 3, PROJECT_CONTEXT_FRAGMENT_PATH = ".hasna/instructions/project-context.md", PROJECT_CONTEXT_MANIFEST_PATH = ".hasna/projects/project-context-manifest.json", PROJECT_CONTEXT_CACHE_PATH = ".hasna/projects/project-context-cache.json", PROJECT_CONTEXT_LOCK_PATH = ".hasna/projects/project-context.lock", PROJECT_CONTEXT_SNAPSHOT_DIR = ".hasna/projects/project-context-snapshots", PROJECT_CONTEXT_CACHE_SCHEMA = "hasna.instructions.project-context-cache/v1", PROJECT_CONTEXT_MANAGED_COMMENT = "Managed by @hasna/configs project context", SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES, FOREIGN_INPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_MAX_BYTES, SESSION_MANAGED_INPUT_MAX_BYTES, SESSION_MANAGED_OUTPUT_PATHS, SESSION_MANAGED_OUTPUT_WARN_BYTES, PROJECT_CONTEXT_LOCK_STALE_MS, LEGACY_CONFIGS_PACKAGE = "@hasna/configs", LEGACY_CONFIGS_COMPAT_VERSION = "0.2.45", LEGACY_CONFIGS_EXECUTABLE = "configs", PROJECT_KINDS, PROJECT_STATUSES, LINK_STATES, RESOLUTION_SOURCES, safeId, nullableId, producerSlug, producerName, safeOptionalDisplay, isoTimestamp, revisionSchema, hashSchema, absolutePath, commandArg, financeText, financeLegalEntity, financeProjectMetadataSchema, commandSchema, projectContextBundleSchema, storedManifestProjectContextSchema, storedManifestFileSchema, storedManifestObservationSchema, projectContextMetadataSnapshotSchema, projectContextCacheSchema, ProjectContextError, ProjectContextHashRace, anchoredFsOps, atomicExchange, atomicExchangeLibraries;
7296
7354
  var init_project_context = __esm(() => {
7297
7355
  init_zod();
7298
7356
  init_redact();
@@ -7305,6 +7363,7 @@ var init_project_context = __esm(() => {
7305
7363
  SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES = 8 * 1024 * 1024;
7306
7364
  FOREIGN_INPUT_MAX_BYTES = 256 * 1024;
7307
7365
  SESSION_MANAGED_OUTPUT_MAX_BYTES = SESSION_COMPATIBILITY_MANIFEST_MAX_BYTES;
7366
+ SESSION_MANAGED_INPUT_MAX_BYTES = SESSION_MANAGED_OUTPUT_MAX_BYTES;
7308
7367
  SESSION_MANAGED_OUTPUT_PATHS = [
7309
7368
  ".hasna/session-render-manifest.json",
7310
7369
  ".codewith/.hasna/session-render-manifest.json",
@@ -7604,6 +7663,9 @@ function buildCodexAgentsMd(source, context = {}) {
7604
7663
  function buildOpenCodeAgentsMd(source, context = {}) {
7605
7664
  return flattenWithRules(source, context);
7606
7665
  }
7666
+ function buildSumiAgentsMd(source, context = {}) {
7667
+ return flattenWithRules(source, context);
7668
+ }
7607
7669
  function buildCursorMdc(source) {
7608
7670
  const stem = source.target_path ? basename2(source.target_path, extname(source.target_path)) : source.slug;
7609
7671
  const description = source.name || stem;
@@ -7632,6 +7694,8 @@ function applyTransform(source, output, context = {}) {
7632
7694
  return buildCodexAgentsMd(source, context);
7633
7695
  case "opencode-flat":
7634
7696
  return buildOpenCodeAgentsMd(source, context);
7697
+ case "sumi-flat":
7698
+ return buildSumiAgentsMd(source, context);
7635
7699
  case "cursor-mdc":
7636
7700
  return buildCursorMdc(source);
7637
7701
  case "skill-neutral":
@@ -7759,6 +7823,10 @@ function normalizeProfileAssetBinding(value) {
7759
7823
  const rollback = record["rollback"];
7760
7824
  if (!ASSET_ROLLBACK_POLICIES.includes(rollback))
7761
7825
  throw new Error(`Invalid asset rollback policy: ${String(rollback)}`);
7826
+ const nativeAgent = record["nativeAgent"] === undefined ? undefined : normalizeNativeAgentMetadata(record["nativeAgent"]);
7827
+ if (nativeAgent && (kind !== "custom-agent" || strategy !== "emit-file" || !["claude", "sumi", "opencode"].includes(provider))) {
7828
+ throw new Error("nativeAgent metadata requires a supported emitted custom-agent definition.");
7829
+ }
7762
7830
  return {
7763
7831
  schema: PROFILE_ASSET_BINDING_SCHEMA,
7764
7832
  assetKey,
@@ -7778,6 +7846,7 @@ function normalizeProfileAssetBinding(value) {
7778
7846
  root,
7779
7847
  relativePath: nonEmptyString(destinationRecord["relativePath"], "destination.relativePath")
7780
7848
  },
7849
+ ...nativeAgent ? { nativeAgent } : {},
7781
7850
  uninstall,
7782
7851
  rollback
7783
7852
  };
@@ -7853,6 +7922,7 @@ function compileAssetPlan(input) {
7853
7922
  allowed: binding.source.allowed
7854
7923
  },
7855
7924
  destination: binding.destination,
7925
+ ...binding.nativeAgent ? { nativeAgent: binding.nativeAgent } : {},
7856
7926
  support,
7857
7927
  action,
7858
7928
  mutationMode: input.mode,
@@ -7908,6 +7978,18 @@ function validateAssetSourceAndDestination(bundle, binding, diagnostics) {
7908
7978
  pathSafe = false;
7909
7979
  diagnostics.push(diagnostic("error", "ASSET_DESTINATION_UNSAFE", binding.assetKey, error.message));
7910
7980
  }
7981
+ if (pathSafe && binding.kind === "custom-agent" && binding.destination.strategy === "emit-file" && (binding.selector.provider === "claude" || binding.selector.provider === "sumi" || binding.nativeAgent) && (binding.destination.root !== "target-home" || !/^agents\/[A-Za-z0-9][A-Za-z0-9_-]*\.md$/.test(safeRelativePath(binding.destination.relativePath)))) {
7982
+ pathSafe = false;
7983
+ diagnostics.push(diagnostic("error", "ASSET_CUSTOM_AGENT_DESTINATION_UNSUPPORTED", binding.assetKey, "Native custom-agent definitions require agents/<name>.md beneath an explicit target-home."));
7984
+ }
7985
+ if (binding.nativeAgent) {
7986
+ if (posix.basename(binding.destination.relativePath, ".md") !== binding.nativeAgent.name) {
7987
+ diagnostics.push(diagnostic("error", "ASSET_NATIVE_AGENT_NAME_MISMATCH", binding.assetKey, "Native role name must match its destination filename."));
7988
+ }
7989
+ if (/^\uFEFF?---[ \t]*\r?\n/.test(bundle.content)) {
7990
+ diagnostics.push(diagnostic("error", "ASSET_NATIVE_AGENT_DOUBLE_HEADER", binding.assetKey, "Canonical role prose must not already contain frontmatter when nativeAgent metadata is supplied."));
7991
+ }
7992
+ }
7911
7993
  const expectedLocator = bundle.locator;
7912
7994
  if (!binding.source.immutable || binding.source.locator !== expectedLocator) {
7913
7995
  diagnostics.push(diagnostic("error", "ASSET_SOURCE_MUTABLE_OR_UNPINNED", binding.assetKey, `Asset source must be pinned to ${expectedLocator}.`));
@@ -7963,6 +8045,72 @@ function safeRelativePath(value) {
7963
8045
  }
7964
8046
  return normalized;
7965
8047
  }
8048
+ function normalizeNativeAgentMetadata(value) {
8049
+ const record = objectRecord(value, "nativeAgent");
8050
+ if (Object.keys(record).some((key) => !["name", "description", "frontmatter"].includes(key)))
8051
+ throw new Error("Unknown nativeAgent metadata field.");
8052
+ const name = nonEmptyString(record["name"], "nativeAgent.name");
8053
+ const description = nonEmptyString(record["description"], "nativeAgent.description");
8054
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(name) || name.length > 128)
8055
+ throw new Error("Invalid nativeAgent.name.");
8056
+ if (description.length > 4096 || /[\r\n\0]/.test(description))
8057
+ throw new Error("Invalid nativeAgent.description.");
8058
+ if (record["frontmatter"] === undefined)
8059
+ return { name, description };
8060
+ const frontmatter = record["frontmatter"];
8061
+ if (typeof frontmatter !== "string" || frontmatter.length > 16384 || frontmatter.includes("\x00"))
8062
+ throw new Error("Invalid nativeAgent.frontmatter.");
8063
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n$/.exec(frontmatter);
8064
+ if (!match)
8065
+ throw new Error("nativeAgent.frontmatter requires one complete newline-terminated YAML header.");
8066
+ const scalar = (raw) => {
8067
+ if (raw.startsWith('"')) {
8068
+ try {
8069
+ const parsed = JSON.parse(raw);
8070
+ return typeof parsed === "string" ? parsed : undefined;
8071
+ } catch {
8072
+ return;
8073
+ }
8074
+ }
8075
+ if (!/^[A-Za-z0-9][A-Za-z0-9 _.,:;()/?@+-]*$/.test(raw) || /:(?:\s|$)/.test(raw))
8076
+ return;
8077
+ if (/^(?:true|yes|on|y)$/i.test(raw))
8078
+ return true;
8079
+ if (/^(?:false|no|off|n)$/i.test(raw))
8080
+ return false;
8081
+ if (/^null$/i.test(raw))
8082
+ return null;
8083
+ if (/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(raw))
8084
+ return Number(raw);
8085
+ if (/^[0-9]/.test(raw))
8086
+ return;
8087
+ return raw;
8088
+ };
8089
+ const fields = new Map;
8090
+ for (const line of match[1].split(/\r?\n/)) {
8091
+ if (!line.trim() || line.startsWith("#"))
8092
+ continue;
8093
+ const field = /^([A-Za-z][A-Za-z0-9_-]*):[ \t]+(.+)$/.exec(line);
8094
+ if (!field || fields.has(field[1]) || scalar(field[2]) === undefined)
8095
+ throw new Error("nativeAgent.frontmatter requires unambiguous, unique flat scalar fields.");
8096
+ fields.set(field[1], scalar(field[2]));
8097
+ }
8098
+ if (fields.get("name") !== name || fields.get("description") !== description)
8099
+ throw new Error("nativeAgent.frontmatter name and description must match explicit metadata.");
8100
+ return { name, description, frontmatter };
8101
+ }
8102
+ function renderNativeAgentContent(content, metadata) {
8103
+ if (!metadata)
8104
+ return content;
8105
+ const value = normalizeNativeAgentMetadata(metadata);
8106
+ if (/^\uFEFF?---[ \t]*\r?\n/.test(content))
8107
+ throw new Error("Canonical role prose already contains frontmatter.");
8108
+ return (value.frontmatter ?? `---
8109
+ name: ${JSON.stringify(value.name)}
8110
+ description: ${JSON.stringify(value.description)}
8111
+ ---
8112
+ `) + content;
8113
+ }
7966
8114
  function diagnostic(severity, code, assetKey, message) {
7967
8115
  return { severity, code, assetKey, message };
7968
8116
  }
@@ -8008,6 +8156,7 @@ var init_asset_plan = __esm(() => {
8008
8156
  ASSET_CAPABILITY_DESCRIPTORS = Object.freeze([
8009
8157
  assetCapability("claude", "code", "skill", "supported", ["emit-file"], "Claude Code project or profile skill files."),
8010
8158
  assetCapability("claude", "code", "workflow", "supported", ["emit-file"], "Claude Code command and workflow files."),
8159
+ assetCapability("claude", "code", "custom-agent", "supported", ["emit-file"], "Claude Code Markdown subagents with native frontmatter in agents/.", ">=2.1.276 <3.0.0"),
8011
8160
  assetCapability("claude", "code", "hook", "supported", ["emit-file"], "Claude Code hook configuration fragments."),
8012
8161
  assetCapability("claude", "code", "plugin", "conditional", ["install-marketplace"], "Claude marketplace installation requires an explicit installer."),
8013
8162
  assetCapability("codex", "cli", "skill", "supported", ["emit-file"], "Codex skill bundle files."),
@@ -8018,6 +8167,7 @@ var init_asset_plan = __esm(() => {
8018
8167
  assetCapability("opencode", "cli", "workflow", "supported", ["emit-file"], "OpenCode command/workflow files."),
8019
8168
  assetCapability("opencode", "cli", "plugin", "supported", ["emit-file"], "OpenCode local plugin modules."),
8020
8169
  assetCapability("opencode", "cli", "custom-agent", "supported", ["emit-file"], "OpenCode custom agent files."),
8170
+ assetCapability("sumi", "cli", "custom-agent", "supported", ["emit-file"], "Sumi Markdown agent definitions in its explicit resolved config or project directory.", ">=0.2.22 <0.3.0"),
8021
8171
  assetCapability("codewith", "cli", "skill", "supported", ["emit-file"], "Codewith skill bundle files."),
8022
8172
  assetCapability("codewith", "cli", "plugin", "conditional", ["install-local", "install-marketplace"], "Codewith plugin installation requires an explicit installer."),
8023
8173
  assetCapability("aicopilot", "cli", "skill", "supported", ["emit-file"], "AICopilot discovered skill files."),
@@ -8050,6 +8200,7 @@ var init_asset_plan = __esm(() => {
8050
8200
  codex: "cli",
8051
8201
  cursor: "ide",
8052
8202
  opencode: "cli",
8203
+ sumi: "cli",
8053
8204
  codewith: "cli",
8054
8205
  qwen: "cli",
8055
8206
  aicopilot: "cli",
@@ -8698,9 +8849,19 @@ function skippedSource(source, reason) {
8698
8849
  };
8699
8850
  }
8700
8851
  function normalizeSources(sources, tool, allowEmptySources) {
8701
- const normalized = sources.map((source, index) => {
8852
+ const providerSkipped = [];
8853
+ const normalized = sources.flatMap((source, index) => {
8702
8854
  if (!source.id.trim())
8703
8855
  throw new Error("Session instruction source id is required.");
8856
+ if (source.targetProviders?.length && !providerTargetsTool(source.targetProviders, tool)) {
8857
+ providerSkipped.push({
8858
+ id: source.id,
8859
+ label: source.label ?? source.id,
8860
+ targetProviders: source.targetProviders,
8861
+ reason: "instruction source targets a different provider"
8862
+ });
8863
+ return [];
8864
+ }
8704
8865
  const floored = applyAgentOperatingRulesFloor(source, source.content ?? "");
8705
8866
  const content = filterProviderOnlyBlocks(floored.content, tool);
8706
8867
  const normalized2 = {
@@ -8719,7 +8880,7 @@ function normalizeSources(sources, tool, allowEmptySources) {
8719
8880
  if (!allowEmptySources && !normalized2.content.trim() && normalized2.resolvedRules.length === 0 && !hasPathReferences) {
8720
8881
  throw new Error(`Session instruction source "${source.id}" is empty. Pass --allow-empty-sources only for explicit empty renders.`);
8721
8882
  }
8722
- return normalized2;
8883
+ return [normalized2];
8723
8884
  });
8724
8885
  const originalOrder = [...normalized].sort(compareSessionInstructionSources);
8725
8886
  const deduplicated = deduplicateSemanticPolicySources(normalized);
@@ -8727,7 +8888,7 @@ function normalizeSources(sources, tool, allowEmptySources) {
8727
8888
  validateTargetedReplacementSources(originalOrder, ordered, deduplicated.skipped, tool);
8728
8889
  rejectDuplicateSourceSlugs(ordered);
8729
8890
  rejectDuplicateRulePaths(ordered);
8730
- return { sources: ordered, skipped: deduplicated.skipped };
8891
+ return { sources: ordered, skipped: [...providerSkipped, ...deduplicated.skipped] };
8731
8892
  }
8732
8893
  function compareSessionInstructionSources(a, b) {
8733
8894
  return SESSION_LAYER_RANK[a.resolvedLayer] - SESSION_LAYER_RANK[b.resolvedLayer] || a.resolvedOrder - b.resolvedOrder || a.id.localeCompare(b.id);
@@ -9155,7 +9316,7 @@ function buildCursorRuleFiles(targetHome, adapter, sources) {
9155
9316
  return [sourceFile, ...ruleFiles];
9156
9317
  });
9157
9318
  }
9158
- function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfig) {
9319
+ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfig, projectRoot) {
9159
9320
  const surface = adapter.providerSurface ?? "legacy-dual";
9160
9321
  const fragments = sources.flatMap((source, index) => [
9161
9322
  makeFile(targetHome, fragmentPath(adapter, index, source), "fragment", sectionForSource(source), [source.id]),
@@ -9175,13 +9336,13 @@ function buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfi
9175
9336
  ]);
9176
9337
  const existingConfigPath = joinTarget(targetHome, adapter.configFile);
9177
9338
  const selectedConfig = existsSync5(existingConfigPath) ? readOpenCodeConfig(readFileSync4(existingConfigPath, "utf8"), existingConfigPath) : providerConfig ? readOpenCodeConfig(providerConfig.content, providerConfig.sourceId) : {};
9178
- const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir));
9339
+ const preservedInstructions = normalizeOpenCodeInstructions(selectedConfig["instructions"]).filter((path) => !pathIsManagedOpenCodeInstruction(path, adapter.managedDir, targetHome, projectRoot));
9179
9340
  const config = {
9180
9341
  ...selectedConfig,
9181
9342
  $schema: typeof selectedConfig["$schema"] === "string" ? selectedConfig["$schema"] : "https://opencode.ai/config.json",
9182
9343
  instructions: [
9183
9344
  ...preservedInstructions,
9184
- ...fragments.map((file) => file.relativePath)
9345
+ ...fragments.map((file) => file.path)
9185
9346
  ]
9186
9347
  };
9187
9348
  const configSourceIds = [
@@ -9224,9 +9385,62 @@ function normalizeOpenCodeInstructions(value) {
9224
9385
  }
9225
9386
  return value;
9226
9387
  }
9227
- function pathIsManagedOpenCodeInstruction(path, managedDir) {
9228
- const normalized = posix2.normalize(path.replaceAll("\\", "/")).replace(/^\.\//, "");
9229
- return normalized === managedDir || normalized.startsWith(`${managedDir}/`);
9388
+ function pathIsManagedOpenCodeInstruction(reference, managedDir, targetHome, projectRoot) {
9389
+ const normalizedManagedDir = posix2.normalize(managedDir.replaceAll("\\", "/"));
9390
+ const candidates = canonicalOpenCodeInstructionPaths(reference, targetHome, projectRoot);
9391
+ return candidates.some((candidate) => candidate === normalizedManagedDir || candidate.endsWith(`/${normalizedManagedDir}`) || candidate.includes(`/${normalizedManagedDir}/`));
9392
+ }
9393
+ function canonicalOpenCodeInstructionPaths(reference, targetHome, projectRoot) {
9394
+ const portable = decodeOpenCodePathEscapes(reference.replaceAll("\\", "/")).replaceAll("\\", "/");
9395
+ if (portable.includes("\x00")) {
9396
+ throw new Error("OpenCode config instruction references cannot contain NUL bytes.");
9397
+ }
9398
+ const windowsAbsolute = /^[A-Za-z]:\//.test(portable);
9399
+ const uriScheme = windowsAbsolute ? null : /^([A-Za-z][A-Za-z0-9+.-]*):/.exec(portable)?.[1]?.toLowerCase();
9400
+ if (uriScheme && uriScheme !== "file")
9401
+ return [];
9402
+ if (uriScheme === "file") {
9403
+ const fileReference = reference.replaceAll("\\", "/");
9404
+ if (!/^file:\/\//i.test(fileReference)) {
9405
+ throw new Error("OpenCode config contains an invalid file URL instruction reference.");
9406
+ }
9407
+ let url;
9408
+ try {
9409
+ url = new URL(fileReference);
9410
+ } catch {
9411
+ throw new Error("OpenCode config contains an invalid file URL instruction reference.");
9412
+ }
9413
+ if (url.protocol !== "file:" || url.username || url.password || url.port || url.search || url.hash) {
9414
+ throw new Error("OpenCode config contains an invalid file URL instruction reference.");
9415
+ }
9416
+ const pathname = decodeOpenCodePathEscapes(url.pathname);
9417
+ if (pathname.includes("\x00")) {
9418
+ throw new Error("OpenCode config instruction references cannot contain NUL bytes.");
9419
+ }
9420
+ const host = url.hostname && url.hostname !== "localhost" ? `//${url.hostname}` : "";
9421
+ return [normalizePortableOpenCodePath(`${host}${pathname}`)];
9422
+ }
9423
+ if (posix2.isAbsolute(portable) || windowsAbsolute || portable.startsWith("//")) {
9424
+ return [normalizePortableOpenCodePath(portable)];
9425
+ }
9426
+ const roots = [
9427
+ ...projectRoot ? [resolveSessionPath(projectRoot)] : [],
9428
+ resolveSessionPath(targetHome)
9429
+ ];
9430
+ return [...new Set(roots.map((root) => normalizePortableOpenCodePath(posix2.join(root.replaceAll("\\", "/"), portable))))];
9431
+ }
9432
+ function decodeOpenCodePathEscapes(value) {
9433
+ return value.replace(/(?:%[a-fA-F0-9]{2})+/g, (encoded) => {
9434
+ try {
9435
+ return decodeURIComponent(encoded);
9436
+ } catch {
9437
+ return encoded;
9438
+ }
9439
+ });
9440
+ }
9441
+ function normalizePortableOpenCodePath(value) {
9442
+ const normalized = posix2.normalize(value.replaceAll("\\", "/"));
9443
+ return normalized.startsWith("//") ? `/${normalized.replace(/^\/+/u, "")}` : normalized;
9230
9444
  }
9231
9445
  function buildAntigravityRuleFiles(targetHome, adapter, sources) {
9232
9446
  return sources.flatMap((source, index) => {
@@ -9280,7 +9494,7 @@ function makeAntigravityRuleFile(targetHome, relativePath, content, sourceIds) {
9280
9494
  }
9281
9495
  return file;
9282
9496
  }
9283
- function buildFiles(targetHome, adapter, profile, sources, providerConfig, providerVersion) {
9497
+ function buildFiles(targetHome, adapter, profile, sources, providerConfig, providerVersion, projectRoot) {
9284
9498
  switch (adapter.mode) {
9285
9499
  case "native-imports":
9286
9500
  return buildNativeImportFiles(targetHome, adapter, profile, sources, providerVersion);
@@ -9289,7 +9503,7 @@ function buildFiles(targetHome, adapter, profile, sources, providerConfig, provi
9289
9503
  case "cursor-mdc":
9290
9504
  return buildCursorRuleFiles(targetHome, adapter, sources);
9291
9505
  case "opencode-instructions":
9292
- return buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfig);
9506
+ return buildOpenCodeFiles(targetHome, adapter, profile, sources, providerConfig, projectRoot);
9293
9507
  case "antigravity-rules":
9294
9508
  return buildAntigravityRuleFiles(targetHome, adapter, sources);
9295
9509
  case "provider-rules":
@@ -9318,12 +9532,13 @@ function buildAssetFiles(input, targetHome, blocked) {
9318
9532
  if (!relativePath || relativePath === ".." || relativePath.startsWith("../") || isAbsolute3(relativePath)) {
9319
9533
  throw new Error(`Asset ${item.assetKey} is outside the session snapshot root; use a project-scoped session plan for atomic application.`);
9320
9534
  }
9535
+ const renderedContent = renderNativeAgentContent(content, item.nativeAgent);
9321
9536
  return {
9322
9537
  path,
9323
9538
  relativePath: assertSafeRelativePath(relativePath),
9324
9539
  role: "asset",
9325
- content,
9326
- sha256: sha2566(content),
9540
+ content: renderedContent,
9541
+ sha256: sha2566(renderedContent),
9327
9542
  sourceIds: [item.sourceConfigId, item.assetId]
9328
9543
  };
9329
9544
  });
@@ -9332,6 +9547,11 @@ function isNativeProfileSessionTarget(input) {
9332
9547
  return (input.tool === "grok" || input.tool === "devin") && Boolean(input.targetHome?.trim()) && !input.projectRoot;
9333
9548
  }
9334
9549
  function adapterFor(input) {
9550
+ if (input.tool === "sumi" && input.projectRoot) {
9551
+ if (input.providerSurface)
9552
+ throw new Error(`Provider surface ${input.providerSurface} is not valid for Sumi.`);
9553
+ return Object.freeze({ ...SESSION_TOOL_ADAPTERS.sumi, projectScoped: true, envVar: undefined });
9554
+ }
9335
9555
  if (input.tool === "opencode" && input.providerSurface) {
9336
9556
  if (input.providerSurface !== "opencode-config-instructions" && input.providerSurface !== "opencode-agents-md") {
9337
9557
  throw new Error(`Provider surface ${input.providerSurface} is not valid for OpenCode.`);
@@ -9373,7 +9593,7 @@ function adapterFor(input) {
9373
9593
  }
9374
9594
  if (input.tool !== "codewith")
9375
9595
  return SESSION_TOOL_ADAPTERS[input.tool];
9376
- const gatedNativeImports = input.codewithNativeImports === true || process.env[CODEWITH_NATIVE_IMPORTS_ENV] === "1" || process.env[CODEWITH_NATIVE_IMPORTS_ENV] === "true";
9596
+ const gatedNativeImports = input.codewithNativeImports ?? (process.env[CODEWITH_NATIVE_IMPORTS_ENV] === "1" || process.env[CODEWITH_NATIVE_IMPORTS_ENV] === "true");
9377
9597
  return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
9378
9598
  }
9379
9599
  function getHomeDir() {
@@ -9424,6 +9644,13 @@ function assertSafeTargetRoot(targetHome) {
9424
9644
  return normalized;
9425
9645
  }
9426
9646
  function resolveRenderTarget(input, adapter) {
9647
+ if (input.tool === "sumi" && !input.projectRoot && !input.targetHome?.trim()) {
9648
+ return {
9649
+ targetHome: defaultTargetHome(input.tool, input.profile, input.sessionId),
9650
+ targetKind: "blocked",
9651
+ blockers: ["Sumi requires --target-home from `sumi debug paths config`, or --project-root for repository AGENTS.md."]
9652
+ };
9653
+ }
9427
9654
  if (adapter.projectScoped) {
9428
9655
  if (!input.projectRoot) {
9429
9656
  const label = input.tool === "cursor" ? "Cursor rules" : input.tool === "antigravity" ? "Antigravity rules" : `${adapter.description.split(".")[0]} files`;
@@ -9490,7 +9717,7 @@ function resolveSessionTargetOwnership(input, target) {
9490
9717
  tool: input.tool,
9491
9718
  profile: input.profile,
9492
9719
  targetHome: target.targetHome,
9493
- projectRoot: null,
9720
+ projectRoot: input.tool === "opencode" && input.projectRoot ? resolveSessionPath(input.projectRoot) : null,
9494
9721
  ownedBy: "open-configs",
9495
9722
  canonicalOwner: "instructions",
9496
9723
  writer: {
@@ -9525,6 +9752,14 @@ function planSessionRender(input) {
9525
9752
  const normalized = normalizeSources(input.sources, input.tool, allowEmptySources);
9526
9753
  const composed = composeSources(normalized.sources, input.tool);
9527
9754
  const orderedSources = composed.sources;
9755
+ if (input.tool === "sumi") {
9756
+ for (const source of orderedSources) {
9757
+ const activation = source.metadata?.["activation"];
9758
+ if (source.globs?.length || source.resolvedRules.some((rule) => rule.globs?.length) || activation?.mode && activation.mode !== "always") {
9759
+ throw new Error(`Sumi AGENTS.md cannot preserve conditional activation for source ${source.id}; compile a profile with an explicit supported fallback.`);
9760
+ }
9761
+ }
9762
+ }
9528
9763
  const skippedSources = [
9529
9764
  ...input.skippedSources ?? [],
9530
9765
  ...normalized.skipped,
@@ -9543,7 +9778,7 @@ function planSessionRender(input) {
9543
9778
  if (input.providerConfig && input.tool !== "opencode") {
9544
9779
  throw new Error("Provider base config is supported only for OpenCode session renders.");
9545
9780
  }
9546
- const baseFiles = blocked ? [] : buildFiles(targetHome, adapter, input.profile, orderedSources, input.providerConfig, input.provider_version);
9781
+ const baseFiles = blocked ? [] : buildFiles(targetHome, adapter, input.profile, orderedSources, input.providerConfig, input.provider_version, input.projectRoot);
9547
9782
  const projectContext = blocked ? null : composeProjectContextSessionRender({
9548
9783
  tool: input.tool,
9549
9784
  adapter_mode: adapter.mode,
@@ -9568,6 +9803,7 @@ function planSessionRender(input) {
9568
9803
  adapterMode: adapter.mode,
9569
9804
  profile: input.profile,
9570
9805
  sessionId: input.sessionId ?? null,
9806
+ ...input.refreshSelector ? { refreshSelector: input.refreshSelector } : {},
9571
9807
  targetHome,
9572
9808
  targetKind,
9573
9809
  targetOwner,
@@ -9643,6 +9879,7 @@ function planSessionRender(input) {
9643
9879
  mutationMode: item.mutationMode,
9644
9880
  destination: item.destination,
9645
9881
  digest: item.source.digest,
9882
+ ...item.nativeAgent ? { nativeAgent: item.nativeAgent } : {},
9646
9883
  exactOnceKey: item.exactOnceKey
9647
9884
  }))
9648
9885
  }
@@ -9741,8 +9978,9 @@ function selectProfileConfigsForSessionRender(configs, tool) {
9741
9978
  skippedSources.push(skippedProfileConfig(config, ["opencode"], "provider settings belong to OpenCode"));
9742
9979
  continue;
9743
9980
  }
9744
- if (config.category !== "rules") {
9745
- skippedSources.push(skippedProfileConfig(config, [], config.kind === "reference" ? "reference config is not a provider instruction source" : "profile config is handled by direct config preview/apply"));
9981
+ const sourceRejection = instructionSourceRejection(config);
9982
+ if (sourceRejection) {
9983
+ skippedSources.push(skippedProfileConfig(config, [], sourceRejection));
9746
9984
  continue;
9747
9985
  }
9748
9986
  const output = config.outputs.find((candidate) => candidate.agent === tool);
@@ -9750,7 +9988,7 @@ function selectProfileConfigsForSessionRender(configs, tool) {
9750
9988
  skippedSources.push(skippedProfileConfig(config, [config.agent], "rule targets a different provider"));
9751
9989
  continue;
9752
9990
  }
9753
- const selectedContent = output ? applyTransform(config, output, { configs }) : config.content;
9991
+ const selectedContent = output ? applyTransform(config, output, { configs: [config] }) : config.content;
9754
9992
  sources.push({
9755
9993
  config,
9756
9994
  source: sourceFromConfig({ ...config, content: selectedContent }, sources.length)
@@ -10159,6 +10397,7 @@ var init_session_render = __esm(() => {
10159
10397
  "codex",
10160
10398
  "cursor",
10161
10399
  "opencode",
10400
+ "sumi",
10162
10401
  "codewith",
10163
10402
  "qwen",
10164
10403
  "aicopilot",
@@ -10247,6 +10486,15 @@ var init_session_render = __esm(() => {
10247
10486
  description: "OpenCode AGENTS.md plus opencode.json instructions pointing at managed fragments.",
10248
10487
  providerSurface: "legacy-dual"
10249
10488
  },
10489
+ sumi: {
10490
+ tool: "sumi",
10491
+ mode: "flattened-markdown",
10492
+ indexFile: "AGENTS.md",
10493
+ managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
10494
+ envVar: "SUMI_CONFIG_DIR",
10495
+ nativeImports: false,
10496
+ description: "Sumi native AGENTS.md in the explicit resolved config directory or project root."
10497
+ },
10250
10498
  aicopilot: {
10251
10499
  tool: "aicopilot",
10252
10500
  mode: "flattened-markdown",
@@ -10535,12 +10783,22 @@ function compileInstructionGraph(input) {
10535
10783
  const selector = binding.providers?.find((entry) => entry.provider === input.context.provider || entry.provider === "global");
10536
10784
  if (binding.providers?.length && !selector)
10537
10785
  continue;
10786
+ const output = config.outputs.find((entry) => entry.agent === input.context.provider);
10787
+ if (!binding.providers?.length && config.agent !== "global" && config.agent !== input.context.provider && !output) {
10788
+ diagnostics.push({ severity: "info", code: "SOURCE_PROVIDER_NOT_SELECTED", config_id: configId, message: `${config.slug} targets ${config.agent}, not ${input.context.provider}.` });
10789
+ continue;
10790
+ }
10538
10791
  if (selector?.version_range && !providerVersionSatisfies(input.context.provider_version, selector.version_range)) {
10539
10792
  diagnostics.push({ severity: binding.required ? "error" : "warning", code: "PROVIDER_SELECTOR_VERSION_MISMATCH", config_id: configId, message: `${config.slug} requires ${input.context.provider} ${selector.version_range}; current version is ${input.context.provider_version}.` });
10540
10793
  continue;
10541
10794
  }
10542
10795
  if (!activationMatches(binding.activation, config, input.context))
10543
10796
  continue;
10797
+ const rejection = instructionSourceRejection(config);
10798
+ if (rejection) {
10799
+ diagnostics.push({ severity: !binding.required && binding.fallback === "omit" ? "warning" : "error", code: "SOURCE_NOT_INSTRUCTION", config_id: configId, message: `${config.slug}: ${rejection}.` });
10800
+ continue;
10801
+ }
10544
10802
  if (capability2.activation_modes.includes(binding.activation.mode)) {
10545
10803
  selected.add(configId);
10546
10804
  effective.set(configId, binding.activation);
@@ -10615,7 +10873,9 @@ function compileInstructionGraph(input) {
10615
10873
  assertExactOnce(units, artifacts);
10616
10874
  const sources = units.map((unit, index) => {
10617
10875
  const config = configs.get(unit.config_id);
10618
- return sourceForUnit(config, unit, index);
10876
+ const output = config.outputs.find((entry) => entry.agent === input.context.provider);
10877
+ const content = output ? applyTransform(config, output, { configs: [config] }) : config.content;
10878
+ return sourceForUnit({ ...config, content }, unit, index);
10619
10879
  });
10620
10880
  const planWithoutHash = {
10621
10881
  schema: INSTRUCTION_GRAPH_PLAN_SCHEMA,
@@ -10843,6 +11103,7 @@ var init_instruction_graph = __esm(() => {
10843
11103
  init_types();
10844
11104
  init_session_render();
10845
11105
  init_asset_plan();
11106
+ init_transforms();
10846
11107
  COMMON_FALLBACKS = ["fail", "flatten", "promote-always", "omit"];
10847
11108
  DEFAULT_PROVIDER_CAPABILITIES = Object.freeze({
10848
11109
  claude: capability("claude", ">=1.0.0", "native-import", "CLAUDE.md @ imports plus path-gated rules", {
@@ -10854,6 +11115,7 @@ var init_instruction_graph = __esm(() => {
10854
11115
  codex: capability("codex", ">=0.1.0", "flattened", "AGENTS.md", { asset_surface: "cli" }),
10855
11116
  cursor: capability("cursor", ">=1.0.0", "cursor-rule", ".cursor/rules/*.mdc", { activation_modes: ["always", "glob"], conditional_artifacts: true, asset_surface: "ide" }),
10856
11117
  opencode: capability("opencode", ">=1.0.0", "managed-fragment", "opencode.json instructions", { provider_variant: "v1-instructions", session_surface: "opencode-config-instructions" }),
11118
+ sumi: capability("sumi", ">=0.2.22 <0.3.0", "flattened", "AGENTS.md"),
10857
11119
  codewith: capability("codewith", ">=0.1.0", "flattened", "CODEWITH.md"),
10858
11120
  qwen: capability("qwen", ">=0.1.0", "flattened", "QWEN.md"),
10859
11121
  aicopilot: capability("aicopilot", ">=0.1.0", "flattened", "AICOPILOT.md"),
@@ -13464,6 +13726,7 @@ function listConfigSummariesPage(filter = {}, options = {}, db) {
13464
13726
  })), total, normalized);
13465
13727
  }
13466
13728
  function updateConfig(idOrSlug, input, db) {
13729
+ validateExpectedConfigVersion(input.expected_version);
13467
13730
  const d = db || getDatabase();
13468
13731
  const existing = getConfig(idOrSlug, d);
13469
13732
  const ts = now();
@@ -13519,7 +13782,15 @@ function updateConfig(idOrSlug, input, db) {
13519
13782
  }
13520
13783
  return d.transaction(() => {
13521
13784
  params.push(existing.id);
13522
- d.run(`UPDATE configs SET ${updates.join(", ")} WHERE id = ?`, params);
13785
+ const condition = input.expected_version === undefined ? "" : " AND version = ?";
13786
+ if (input.expected_version !== undefined)
13787
+ params.push(input.expected_version);
13788
+ const result = d.run(`UPDATE configs SET ${updates.join(", ")} WHERE id = ?${condition}`, params);
13789
+ if (result.changes !== 1) {
13790
+ if (input.expected_version !== undefined)
13791
+ throw new ConfigVersionConflictError(existing.id, input.expected_version);
13792
+ throw new ConfigNotFoundError(existing.id);
13793
+ }
13523
13794
  const updated = getConfigById(existing.id, d);
13524
13795
  createSnapshot(updated.id, updated.content, updated.version, d);
13525
13796
  return updated;
@@ -13638,7 +13909,7 @@ function detectMachineContext(overrides = {}) {
13638
13909
  created_at: "",
13639
13910
  os_family: osFamily,
13640
13911
  home_dir: homeDir6,
13641
- workspace_root: overrides.workspace_root ?? join10(homeDir6, osFamily === "macos" ? "Workspace" : "workspace"),
13912
+ workspace_root: overrides.workspace_root ?? "",
13642
13913
  bun_bin_dir: bunBinDir,
13643
13914
  bun_path: overrides.bun_path ?? defaultBunPath,
13644
13915
  path_prefix: overrides.path_prefix ?? (osFamily === "macos" ? `${join10("/opt", "homebrew", "bin")}:${bunBinDir}` : bunBinDir)
@@ -13651,7 +13922,7 @@ function machineContextToVariables(machine) {
13651
13922
  OS_FAMILY: machine.os_family,
13652
13923
  ARCH: machine.arch ?? "",
13653
13924
  HOME_DIR: machine.home_dir,
13654
- WORKSPACE_ROOT: machine.workspace_root,
13925
+ ...machine.workspace_root ? { WORKSPACE_ROOT: machine.workspace_root } : {},
13655
13926
  BUN_BIN_DIR: machine.bun_bin_dir,
13656
13927
  BUN_PATH: machine.bun_path,
13657
13928
  PATH_PREFIX: machine.path_prefix
@@ -14418,7 +14689,9 @@ class CloudConfigStore {
14418
14689
  return data.config;
14419
14690
  }
14420
14691
  async updateConfig(idOrSlug, input) {
14421
- const { data } = await this.request("PATCH", `/configs/${encodeURIComponent(idOrSlug)}`, input);
14692
+ validateExpectedConfigVersion(input.expected_version);
14693
+ const conditional = input.expected_version !== undefined;
14694
+ const { data } = await this.request(conditional ? "POST" : "PATCH", `/configs/${encodeURIComponent(idOrSlug)}${conditional ? "/conditional-update" : ""}`, input);
14422
14695
  return data.config;
14423
14696
  }
14424
14697
  async deleteConfig(idOrSlug) {
@@ -14495,7 +14768,7 @@ class CloudConfigStore {
14495
14768
  throw new ProfileNotFoundError(idOrSlug);
14496
14769
  return parseBoundedOrLegacyPage(data.configs, data.profile.configs, normalized, "profile membership");
14497
14770
  }
14498
- async getProfileConfigBindings(idOrSlug) {
14771
+ async getProfileConfigBindings(idOrSlug, options = {}) {
14499
14772
  let routeMissing = false;
14500
14773
  const bindings = await aggregateBoundedCollection("profile config bindings", async (cursor) => {
14501
14774
  const normalized = normalizeBoundedReadOptions({ limit: 100, cursor });
@@ -14508,6 +14781,8 @@ class CloudConfigStore {
14508
14781
  }, (binding) => `${binding.profile_id}\x00${binding.config_id}`);
14509
14782
  if (!routeMissing)
14510
14783
  return bindings;
14784
+ if (options.requireExplicit)
14785
+ throw new Error("HOSTED_BINDINGS_REQUIRED: profile config binding route is unavailable; refusing legacy activation fallback.");
14511
14786
  const profile = await this.getProfile(idOrSlug);
14512
14787
  const configs = await this.getProfileConfigs(idOrSlug);
14513
14788
  return configs.map((config, sort_order) => ({
@@ -14572,7 +14847,7 @@ class CloudConfigStore {
14572
14847
  async removeConfigFromProfile(profileIdOrSlug, configId) {
14573
14848
  await this.request("DELETE", `/profiles/${encodeURIComponent(profileIdOrSlug)}/configs/${encodeURIComponent(configId)}`, undefined, { allow404: true });
14574
14849
  }
14575
- async getProfileAssetBindings(profileIdOrSlug) {
14850
+ async getProfileAssetBindings(profileIdOrSlug, options = {}) {
14576
14851
  let routeMissing = false;
14577
14852
  const assets = await aggregateBoundedCollection("profile asset bindings", async (cursor) => {
14578
14853
  const normalized = normalizeBoundedReadOptions({ limit: 100, cursor });
@@ -14585,6 +14860,8 @@ class CloudConfigStore {
14585
14860
  }, (asset) => `${asset.profile_id}\x00${asset.binding.assetKey}`);
14586
14861
  if (!routeMissing)
14587
14862
  return assets;
14863
+ if (options.requireExplicit)
14864
+ throw new Error("HOSTED_ASSETS_REQUIRED: profile asset binding route is unavailable; refusing an empty asset fallback.");
14588
14865
  await this.getProfile(profileIdOrSlug);
14589
14866
  return [];
14590
14867
  }
@@ -14709,7 +14986,7 @@ init_types();
14709
14986
 
14710
14987
  // src/status.ts
14711
14988
  init_config_store();
14712
- import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
14989
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
14713
14990
 
14714
14991
  // src/lib/apply.ts
14715
14992
  init_types();
@@ -15259,19 +15536,9 @@ function getPackageVersion() {
15259
15536
  }
15260
15537
 
15261
15538
  // src/lib/managed-skill-runtimes.ts
15262
- import { createHash as createHash8 } from "crypto";
15263
- import { spawnSync as spawnSync3 } from "child_process";
15264
- import {
15265
- existsSync as existsSync11,
15266
- lstatSync as lstatSync4,
15267
- mkdirSync as mkdirSync4,
15268
- readFileSync as readFileSync10,
15269
- renameSync as renameSync2,
15270
- rmSync as rmSync3,
15271
- writeFileSync as writeFileSync3
15272
- } from "fs";
15539
+ import { lstatSync as lstatSync4 } from "fs";
15273
15540
  import { homedir as homedir8 } from "os";
15274
- import { dirname as dirname8, join as join14, parse as parse4, relative as relative5, resolve as resolve10 } from "path";
15541
+ import { join as join14, resolve as resolve10 } from "path";
15275
15542
  var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
15276
15543
  var INBOX_SKILL_MARKERS = [
15277
15544
  [".claude", "skills", "inbox", "SKILL.md"],
@@ -15280,369 +15547,67 @@ var INBOX_SKILL_MARKERS = [
15280
15547
  [".config", "opencode", "skills", "inbox", "SKILL.md"],
15281
15548
  [".cursor", "skills", "inbox", "SKILL.md"]
15282
15549
  ];
15283
- var REQUIRED_WATCH_FLAGS = ["--from <agent>", "--all", "--full-content"];
15284
- function sha2568(content) {
15285
- return createHash8("sha256").update(content).digest("hex");
15286
- }
15287
- function lstatOrNull(path) {
15288
- try {
15289
- return lstatSync4(path);
15290
- } catch {
15291
- return null;
15292
- }
15293
- }
15294
- function findSymlinkedAncestor(path) {
15295
- const normalized = resolve10(path);
15296
- const parsed = parse4(normalized);
15297
- let current = parsed.root;
15298
- const rel = relative5(parsed.root, normalized);
15299
- for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
15550
+ function needsMigrationReview(homeDir6, parts) {
15551
+ let current = resolve10(homeDir6);
15552
+ const absolute = join14(current, ...parts);
15553
+ for (const segment of parts) {
15300
15554
  current = join14(current, segment);
15301
- if (!existsSync11(current))
15302
- return null;
15303
- if (lstatSync4(current).isSymbolicLink())
15304
- return current;
15305
- }
15306
- return null;
15307
- }
15308
- function assertNoSymlinkAncestors2(path) {
15309
- const found = findSymlinkedAncestor(path);
15310
- if (found !== null) {
15311
- throw new Error(`managed skill path uses a symlink ancestor: ${found}`);
15312
- }
15313
- }
15314
- function packagedInboxSkillPath(explicitPath) {
15315
- if (explicitPath)
15316
- return explicitPath;
15317
- throw new Error("Bundled skill contracts are retired; use the Skills CLI to manage private skills");
15318
- }
15319
- function readCanonicalSkill(explicitPath) {
15320
- const assetPath = packagedInboxSkillPath(explicitPath);
15321
- const stat = lstatOrNull(assetPath);
15322
- if (!stat?.isFile()) {
15323
- throw new Error("packaged inbox skill contract is not a regular file");
15324
- }
15325
- const content = readFileSync10(assetPath, "utf8");
15326
- if (!content.includes("conversations watch --from <agent> --all")) {
15327
- throw new Error("packaged inbox skill contract does not declare the canonical conversations watcher");
15328
- }
15329
- if (!content.includes("There is no separate")) {
15330
- throw new Error("packaged inbox skill contract does not retire the legacy executable");
15331
- }
15332
- return { content, sha256: sha2568(content) };
15333
- }
15334
- function runProbe(command, args) {
15335
- const result = spawnSync3(command, args, {
15336
- encoding: "utf8",
15337
- timeout: 5000,
15338
- stdio: ["ignore", "pipe", "pipe"]
15339
- });
15340
- if (result.error || result.status !== 0) {
15341
- return { ok: false, output: "" };
15342
- }
15343
- return {
15344
- ok: true,
15345
- output: `${result.stdout ?? ""}
15346
- ${result.stderr ?? ""}`.trim()
15347
- };
15348
- }
15349
- function parseVersion(output) {
15350
- return output.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
15351
- }
15352
- function compareVersions(left, right) {
15353
- const a = left.split(".").map(Number);
15354
- const b = right.split(".").map(Number);
15355
- for (let i = 0;i < Math.max(a.length, b.length); i++) {
15356
- const delta = (a[i] ?? 0) - (b[i] ?? 0);
15357
- if (delta !== 0)
15358
- return delta;
15359
- }
15360
- return 0;
15361
- }
15362
- function inspectSkillMarkers(homeDir6) {
15363
- return INBOX_SKILL_MARKERS.map((parts) => join14(homeDir6, ...parts)).map((path) => {
15364
- const stat = lstatOrNull(path);
15365
- if (!stat)
15366
- return null;
15367
- if (!stat.isFile()) {
15368
- return { path, content: null, mode: null, regular: false };
15555
+ let stat;
15556
+ try {
15557
+ stat = lstatSync4(current);
15558
+ } catch (error) {
15559
+ if (error.code === "ENOENT")
15560
+ return false;
15561
+ throw error;
15369
15562
  }
15370
- return {
15371
- path,
15372
- content: readFileSync10(path, "utf8"),
15373
- mode: stat.mode & 511,
15374
- regular: true
15375
- };
15376
- }).filter((snapshot) => snapshot !== null);
15563
+ if (stat.isSymbolicLink() || current !== absolute && !stat.isDirectory())
15564
+ return true;
15565
+ }
15566
+ return true;
15377
15567
  }
15378
- function inspectInbox(options) {
15568
+ function inspectLegacyInbox(options) {
15379
15569
  const homeDir6 = options.homeDir ?? homedir8();
15380
- const runtimeCommand = options.conversationsCommand ?? "conversations";
15381
- const snapshots = inspectSkillMarkers(homeDir6);
15382
- const skillPresent = snapshots.length > 0;
15383
- let canonicalContent = null;
15384
- let canonicalSha256 = null;
15385
- let assetError = null;
15386
- try {
15387
- const canonical = readCanonicalSkill(options.assetPath);
15388
- canonicalContent = canonical.content;
15389
- canonicalSha256 = canonical.sha256;
15390
- } catch (error) {
15391
- assetError = error instanceof Error ? error.message : String(error);
15392
- }
15393
- const versionProbe = skillPresent ? runProbe(runtimeCommand, ["--version"]) : { ok: false, output: "" };
15394
- const helpProbe = versionProbe.ok ? runProbe(runtimeCommand, ["watch", "--help"]) : { ok: false, output: "" };
15395
- const runtimeVersion = versionProbe.ok ? parseVersion(versionProbe.output) : null;
15396
- const supportsFrom = helpProbe.ok && helpProbe.output.includes(REQUIRED_WATCH_FLAGS[0]);
15397
- const supportsAll = helpProbe.ok && helpProbe.output.includes(REQUIRED_WATCH_FLAGS[1]);
15398
- const supportsFullContent = helpProbe.ok && helpProbe.output.includes(REQUIRED_WATCH_FLAGS[2]);
15399
- const packageReady = versionProbe.ok && runtimeVersion !== null && compareVersions(runtimeVersion, INBOX_CONVERSATIONS_MINIMUM_VERSION) >= 0 && helpProbe.ok && supportsFrom && supportsAll && supportsFullContent;
15400
- const heartbeatProbe = skillPresent && packageReady && options.agent ? runProbe(runtimeCommand, ["agents", "heartbeat", "--from", options.agent, "--json"]) : null;
15401
- const hostedHeartbeat = heartbeatProbe === null ? "unverified" : heartbeatProbe.ok ? "passed" : "failed";
15402
- const deliveryVerified = hostedHeartbeat === "passed" && options.deliveryVerified === true;
15403
- const staleMarkers = canonicalContent === null ? snapshots.map((snapshot) => snapshot.path) : snapshots.filter((snapshot) => !snapshot.regular || snapshot.content !== canonicalContent).map((snapshot) => snapshot.path);
15404
- let reason = "skill not installed";
15405
- if (skillPresent) {
15406
- const nonRegular = snapshots.some((snapshot) => !snapshot.regular);
15407
- const symlinkAncestor = snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname8(path))).find((found) => found !== null);
15408
- if (nonRegular)
15409
- reason = "managed skill target is not a regular file";
15410
- else if (symlinkAncestor)
15411
- reason = `managed skill path uses a symlink ancestor: ${symlinkAncestor}`;
15412
- else if (assetError)
15413
- reason = assetError;
15414
- else if (!versionProbe.ok)
15415
- reason = "conversations command unavailable";
15416
- else if (!runtimeVersion)
15417
- reason = "conversations version is unreadable";
15418
- else if (compareVersions(runtimeVersion, INBOX_CONVERSATIONS_MINIMUM_VERSION) < 0) {
15419
- reason = `conversations ${runtimeVersion} is older than ${INBOX_CONVERSATIONS_MINIMUM_VERSION}`;
15420
- } else if (!helpProbe.ok)
15421
- reason = "conversations watch help is unavailable";
15422
- else if (!supportsFrom || !supportsAll || !supportsFullContent) {
15423
- const missing = [
15424
- !supportsFrom ? "--from" : null,
15425
- !supportsAll ? "--all" : null,
15426
- !supportsFullContent ? "--full-content" : null
15427
- ].filter((flag) => flag !== null);
15428
- reason = `conversations watch is missing required flags: ${missing.join(", ")}`;
15429
- } else if (staleMarkers.length > 0)
15430
- reason = "skill contract stale";
15431
- else if (hostedHeartbeat === "failed")
15432
- reason = "hosted heartbeat failed; manual fallback required";
15433
- else if (hostedHeartbeat === "unverified")
15434
- reason = "hosted heartbeat unverified; manual fallback required";
15435
- else if (!deliveryVerified)
15436
- reason = "hosted heartbeat passed; channel and DM delivery verification required";
15437
- else
15438
- reason = "ready";
15439
- }
15570
+ const markers = INBOX_SKILL_MARKERS.filter((parts) => needsMigrationReview(homeDir6, parts)).map((parts) => join14(homeDir6, ...parts));
15571
+ const present = markers.length > 0;
15440
15572
  return {
15441
- status: {
15442
- skill: "inbox",
15443
- runtime: "conversations watch",
15444
- minimum_version: INBOX_CONVERSATIONS_MINIMUM_VERSION,
15445
- skill_present: skillPresent,
15446
- skill_markers: snapshots.map((snapshot) => snapshot.path),
15447
- skill_contracts_current: snapshots.length - staleMarkers.length,
15448
- stale_skill_markers: staleMarkers,
15449
- expected_skill_sha256: canonicalSha256,
15450
- runtime_command: runtimeCommand,
15451
- runtime_present: versionProbe.ok,
15452
- runtime_version: runtimeVersion,
15453
- watch_supports_from: supportsFrom,
15454
- watch_supports_all: supportsAll,
15455
- watch_supports_full_content: supportsFullContent,
15456
- hosted_heartbeat: hostedHeartbeat,
15457
- delivery_verified: deliveryVerified,
15458
- manual_fallback_ready: skillPresent && staleMarkers.length === 0 && packageReady,
15459
- healthy: !skillPresent || reason === "ready",
15460
- reason
15461
- },
15462
- canonicalContent,
15463
- snapshots
15573
+ skill: "inbox",
15574
+ runtime: "conversations watch",
15575
+ minimum_version: INBOX_CONVERSATIONS_MINIMUM_VERSION,
15576
+ skill_present: present,
15577
+ skill_markers: markers,
15578
+ skill_contracts_current: 0,
15579
+ stale_skill_markers: markers,
15580
+ expected_skill_sha256: null,
15581
+ runtime_command: options.conversationsCommand ?? "conversations",
15582
+ runtime_present: false,
15583
+ runtime_version: null,
15584
+ watch_supports_from: false,
15585
+ watch_supports_all: false,
15586
+ watch_supports_full_content: false,
15587
+ hosted_heartbeat: "unverified",
15588
+ delivery_verified: false,
15589
+ manual_fallback_ready: false,
15590
+ healthy: !present,
15591
+ reason: present ? "Native Inbox skill management is retired; use the Skills CLI to review skills migrate native, then sync and load the selected skills" : "No legacy native Inbox skill found; skill availability is managed by the Skills CLI"
15464
15592
  };
15465
15593
  }
15466
15594
  function inspectManagedSkillRuntimes(options = {}) {
15467
- const runtime = inspectInbox(options).status;
15468
- const installed = runtime.skill_present ? [runtime] : [];
15595
+ const runtime = inspectLegacyInbox(options);
15469
15596
  return {
15470
15597
  runtimes: [runtime],
15471
- skills_present: installed.length,
15472
- healthy: installed.filter((item) => item.healthy).length,
15473
- missing: installed.filter((item) => !item.healthy).length
15474
- };
15475
- }
15476
- function runtimeReadyForWrite(status) {
15477
- return status.runtime_present && status.runtime_version !== null && compareVersions(status.runtime_version, INBOX_CONVERSATIONS_MINIMUM_VERSION) >= 0 && status.watch_supports_from && status.watch_supports_all && status.watch_supports_full_content;
15478
- }
15479
- function projectUpdatedStatus(status, contractCount) {
15480
- const healthy = status.hosted_heartbeat === "passed" && status.delivery_verified;
15481
- return {
15482
- ...status,
15483
- skill_contracts_current: contractCount,
15484
- stale_skill_markers: [],
15485
- manual_fallback_ready: true,
15486
- healthy,
15487
- reason: healthy ? "ready" : status.hosted_heartbeat === "failed" ? "hosted heartbeat failed; manual fallback required" : status.hosted_heartbeat === "unverified" ? "hosted heartbeat unverified; manual fallback required" : "hosted heartbeat passed; channel and DM delivery verification required"
15598
+ skills_present: runtime.skill_present ? 1 : 0,
15599
+ healthy: 0,
15600
+ missing: runtime.skill_present ? 1 : 0
15488
15601
  };
15489
15602
  }
15490
- function cleanup(path) {
15491
- rmSync3(path, { force: true });
15492
- }
15493
- function writeAtomic(path, content, mode) {
15494
- assertNoSymlinkAncestors2(dirname8(path));
15495
- const tempPath = `${path}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
15496
- try {
15497
- mkdirSync4(dirname8(path), { recursive: true, mode: 493 });
15498
- writeFileSync3(tempPath, content, { mode, flag: "wx" });
15499
- renameSync2(tempPath, path);
15500
- } finally {
15501
- cleanup(tempPath);
15502
- }
15503
- }
15504
- var DEFAULT_SKILL_WRITE_FILE_OPERATIONS = {
15505
- lstat: lstatOrNull,
15506
- read: (path) => readFileSync10(path, "utf8"),
15507
- write: writeAtomic
15508
- };
15509
- function writeSkillContractsTransactional(snapshots, canonicalContent, fileOperations = DEFAULT_SKILL_WRITE_FILE_OPERATIONS) {
15510
- const written = [];
15511
- try {
15512
- for (const snapshot of snapshots) {
15513
- const currentStat = fileOperations.lstat(snapshot.path);
15514
- if (!currentStat?.isFile() || fileOperations.read(snapshot.path) !== snapshot.content) {
15515
- throw new Error("managed skill changed after inspection; refusing a stale write");
15516
- }
15517
- fileOperations.write(snapshot.path, canonicalContent, snapshot.mode);
15518
- written.push(snapshot);
15519
- }
15520
- return { ok: true, error: null, rollback_conflicts: [] };
15521
- } catch (error) {
15522
- const rollbackConflicts = [];
15523
- for (const snapshot of written.reverse()) {
15524
- const currentStat = fileOperations.lstat(snapshot.path);
15525
- if (!currentStat?.isFile()) {
15526
- rollbackConflicts.push(`${snapshot.path}: no longer a regular file`);
15527
- continue;
15528
- }
15529
- let currentContent;
15530
- try {
15531
- currentContent = fileOperations.read(snapshot.path);
15532
- } catch {
15533
- rollbackConflicts.push(`${snapshot.path}: could not read the current file`);
15534
- continue;
15535
- }
15536
- if (currentContent !== canonicalContent) {
15537
- rollbackConflicts.push(`${snapshot.path}: changed after this reconciliation wrote it`);
15538
- continue;
15539
- }
15540
- try {
15541
- fileOperations.write(snapshot.path, snapshot.content, snapshot.mode);
15542
- } catch {
15543
- rollbackConflicts.push(`${snapshot.path}: still owned but could not be restored`);
15544
- }
15545
- }
15546
- return {
15547
- ok: false,
15548
- error: error instanceof Error ? error.message : String(error),
15549
- rollback_conflicts: rollbackConflicts
15550
- };
15551
- }
15552
- }
15553
15603
  async function reconcileManagedSkillRuntimes(options = {}) {
15604
+ const runtime = inspectLegacyInbox(options);
15554
15605
  const dryRun = options.dryRun ?? false;
15555
- const before = inspectInbox(options);
15556
- const status = before.status;
15557
- if (!status.skill_present) {
15558
- return {
15559
- runtimes: [{ ...status, action: "skipped", dry_run: dryRun, skill_contracts_changed: 0 }],
15560
- changed: 0,
15561
- failed: 0,
15562
- dry_run: dryRun
15563
- };
15564
- }
15565
- if (before.snapshots.some((snapshot) => !snapshot.regular)) {
15566
- return {
15567
- runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
15568
- changed: 0,
15569
- failed: 1,
15570
- dry_run: dryRun
15571
- };
15572
- }
15573
- const symlinkedAncestor = before.snapshots.map((snapshot) => snapshot.path).map((path) => findSymlinkedAncestor(dirname8(path))).find((found) => found !== null);
15574
- if (symlinkedAncestor) {
15575
- return {
15576
- runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
15577
- changed: 0,
15578
- failed: 1,
15579
- dry_run: dryRun
15580
- };
15581
- }
15582
- if (!before.canonicalContent || !runtimeReadyForWrite(status)) {
15583
- return {
15584
- runtimes: [{ ...status, action: "failed", dry_run: dryRun, skill_contracts_changed: 0 }],
15585
- changed: 0,
15586
- failed: 1,
15587
- dry_run: dryRun
15588
- };
15589
- }
15590
- const staleSnapshots = before.snapshots.filter((snapshot) => snapshot.content !== before.canonicalContent);
15591
- if (staleSnapshots.length === 0) {
15592
- return {
15593
- runtimes: [{ ...status, action: "unchanged", dry_run: dryRun, skill_contracts_changed: 0 }],
15594
- changed: 0,
15595
- failed: 0,
15596
- dry_run: dryRun
15597
- };
15598
- }
15599
- if (dryRun) {
15600
- const projected = projectUpdatedStatus(status, before.snapshots.length);
15601
- return {
15602
- runtimes: [{
15603
- ...projected,
15604
- action: "update",
15605
- dry_run: true,
15606
- skill_contracts_changed: staleSnapshots.length
15607
- }],
15608
- changed: 1,
15609
- failed: 0,
15610
- dry_run: true
15611
- };
15612
- }
15613
- const transaction = writeSkillContractsTransactional(staleSnapshots.map((snapshot) => ({
15614
- path: snapshot.path,
15615
- content: snapshot.content,
15616
- mode: snapshot.mode ?? 420
15617
- })), before.canonicalContent);
15618
- if (!transaction.ok) {
15619
- const rollbackConflictReason = transaction.rollback_conflicts.length > 0 ? `; rollback conflicts: ${transaction.rollback_conflicts.join("; ")}` : "";
15620
- const reason = `${transaction.error ?? "managed skill reconciliation failed"}${rollbackConflictReason}`;
15621
- return {
15622
- runtimes: [{
15623
- ...status,
15624
- action: "failed",
15625
- dry_run: false,
15626
- skill_contracts_changed: 0,
15627
- reason
15628
- }],
15629
- changed: 0,
15630
- failed: 1,
15631
- dry_run: false
15632
- };
15633
- }
15634
- const after = inspectInbox(options).status;
15635
- const accepted = after.healthy || after.manual_fallback_ready;
15636
15606
  return {
15637
- runtimes: [{
15638
- ...after,
15639
- action: accepted ? "update" : "failed",
15640
- dry_run: false,
15641
- skill_contracts_changed: accepted ? staleSnapshots.length : 0
15642
- }],
15643
- changed: accepted ? 1 : 0,
15644
- failed: accepted ? 0 : 1,
15645
- dry_run: false
15607
+ runtimes: [{ ...runtime, action: runtime.skill_present ? "failed" : "skipped", dry_run: dryRun, skill_contracts_changed: 0 }],
15608
+ changed: 0,
15609
+ failed: runtime.skill_present ? 1 : 0,
15610
+ dry_run: dryRun
15646
15611
  };
15647
15612
  }
15648
15613
 
@@ -15706,11 +15671,11 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
15706
15671
  continue;
15707
15672
  knownTargets += 1;
15708
15673
  const targetPath = expandPath(config.target_path);
15709
- if (!existsSync12(targetPath)) {
15674
+ if (!existsSync11(targetPath)) {
15710
15675
  missingTargets += 1;
15711
15676
  continue;
15712
15677
  }
15713
- const disk = readFileSync11(targetPath, "utf-8");
15678
+ const disk = readFileSync10(targetPath, "utf-8");
15714
15679
  const { content: redactedDisk } = redactContent(disk, redactFormatForTarget(config.target_path, config.format));
15715
15680
  if (redactedDisk !== config.content) {
15716
15681
  driftedTargets += 1;
@@ -15801,8 +15766,8 @@ async function getConfigsStatus(store = resolveConfigStore(), options = {}) {
15801
15766
  };
15802
15767
  }
15803
15768
  // src/lib/provider-context.ts
15804
- import { createHash as createHash9 } from "crypto";
15805
- import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
15769
+ import { createHash as createHash8 } from "crypto";
15770
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
15806
15771
  import { join as join15 } from "path";
15807
15772
  var PROVIDER_CONTEXT_DIR = ".hasna/provider-context";
15808
15773
  var PROVIDER_CONTEXT_MANIFEST = "manifest.json";
@@ -15938,8 +15903,8 @@ function renderPerEndpointFragment(entry) {
15938
15903
  function renderProviderFragment(entry) {
15939
15904
  return entry ? renderPerEndpointFragment(entry) : INVARIANT_FRAGMENT;
15940
15905
  }
15941
- function sha2569(content) {
15942
- return createHash9("sha256").update(content).digest("hex");
15906
+ function sha2568(content) {
15907
+ return createHash8("sha256").update(content).digest("hex");
15943
15908
  }
15944
15909
  function resolveAndRenderProviderContext(opts) {
15945
15910
  const entry = matchProviderEndpoint(opts.origin);
@@ -15949,17 +15914,17 @@ function resolveAndRenderProviderContext(opts) {
15949
15914
  const reason = entry === null && opts.rawEndpoint ? originAccepted ? `endpoint "${recordedEndpoint}" is not in the provider-context registry; using the invariant fragment` : "endpoint rejected (embedded credentials or unparseable); using the invariant fragment" : null;
15950
15915
  const content = renderProviderFragment(entry);
15951
15916
  const dir = join15(opts.homeDir, PROVIDER_CONTEXT_DIR);
15952
- if (!existsSync13(dir))
15953
- mkdirSync5(dir, { recursive: true });
15917
+ if (!existsSync12(dir))
15918
+ mkdirSync4(dir, { recursive: true });
15954
15919
  const filename = `${entry ? entry.key : "invariant"}.md`;
15955
15920
  const fragmentPath2 = join15(dir, filename);
15956
- const fragmentSha256 = sha2569(content);
15957
- writeFileSync4(fragmentPath2, content, "utf8");
15921
+ const fragmentSha256 = sha2568(content);
15922
+ writeFileSync3(fragmentPath2, content, "utf8");
15958
15923
  const manifestPath = join15(dir, PROVIDER_CONTEXT_MANIFEST);
15959
15924
  let manifest = { schema: PROVIDER_CONTEXT_SCHEMA, fragments: {} };
15960
15925
  try {
15961
- if (existsSync13(manifestPath)) {
15962
- const parsed = JSON.parse(readFileSync12(manifestPath, "utf8"));
15926
+ if (existsSync12(manifestPath)) {
15927
+ const parsed = JSON.parse(readFileSync11(manifestPath, "utf8"));
15963
15928
  if (parsed && typeof parsed === "object")
15964
15929
  manifest = parsed;
15965
15930
  }
@@ -15974,7 +15939,7 @@ function resolveAndRenderProviderContext(opts) {
15974
15939
  rawModel: opts.rawModel || null
15975
15940
  };
15976
15941
  manifest.fragments = fragmentsObj;
15977
- writeFileSync4(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
15942
+ writeFileSync3(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
15978
15943
  return {
15979
15944
  entry,
15980
15945
  rawEndpoint: opts.rawEndpoint,
@@ -16074,10 +16039,10 @@ init_session_render();
16074
16039
 
16075
16040
  // src/lib/station-profile.ts
16076
16041
  init_raw_store_root();
16077
- import { spawnSync as spawnSync4 } from "child_process";
16078
- import { existsSync as existsSync14, lstatSync as lstatSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync13, readdirSync as readdirSync2, writeFileSync as writeFileSync5 } from "fs";
16042
+ import { spawnSync as spawnSync3 } from "child_process";
16043
+ import { existsSync as existsSync13, lstatSync as lstatSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync2, writeFileSync as writeFileSync4 } from "fs";
16079
16044
  import { arch as osArch, homedir as homedir9, hostname as osHostname3, platform as osPlatform, userInfo as osUserInfo } from "os";
16080
- import { dirname as dirname9, join as join16 } from "path";
16045
+ import { dirname as dirname8, join as join16 } from "path";
16081
16046
  var STATION_PROFILE_CACHE_FILENAME = "station-profile.md";
16082
16047
  var STATION_PROFILE_SOURCE_ID = "station-profile";
16083
16048
  var STATION_PROFILE_LAYER = "machine";
@@ -16101,9 +16066,9 @@ function getBunGlobalModulesDir(env = process.env) {
16101
16066
  }
16102
16067
  function readMachinesManifest(path) {
16103
16068
  try {
16104
- if (!existsSync14(path))
16069
+ if (!existsSync13(path))
16105
16070
  return null;
16106
- const parsed = JSON.parse(readFileSync13(path, "utf8"));
16071
+ const parsed = JSON.parse(readFileSync12(path, "utf8"));
16107
16072
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
16108
16073
  return null;
16109
16074
  const machines = parsed["machines"];
@@ -16133,7 +16098,7 @@ function metadataUser(record) {
16133
16098
  }
16134
16099
  function probeMachineStatus(machineId) {
16135
16100
  try {
16136
- const result = spawnSync4("machines", ["details", "--json", "--machine", machineId], {
16101
+ const result = spawnSync3("machines", ["details", "--json", "--machine", machineId], {
16137
16102
  encoding: "utf8",
16138
16103
  timeout: 3000,
16139
16104
  stdio: ["ignore", "pipe", "pipe"]
@@ -16159,7 +16124,7 @@ function resolveStationProfileMachine(env = process.env, options = {}) {
16159
16124
  const record = findLocalManifestMachine(readMachinesManifest(getMachinesManifestPath(env)), hostname2);
16160
16125
  const home = homeDir6(env);
16161
16126
  const platform = stringField(record, "platform") ?? osPlatform();
16162
- const workspacePath = stringField(record, "workspacePath") ?? join16(home, platform === "darwin" ? "Workspace" : "workspace");
16127
+ const workspacePath = stringField(record, "workspacePath");
16163
16128
  const machine = {
16164
16129
  id: stringField(record, "id") ?? hostname2,
16165
16130
  hostname: stringField(record, "hostname") ?? hostname2,
@@ -16178,7 +16143,7 @@ function resolveStationProfileMachine(env = process.env, options = {}) {
16178
16143
  function scopedPackageNames(modulesDir, scope) {
16179
16144
  const scopeDir = join16(modulesDir, scope);
16180
16145
  try {
16181
- if (!existsSync14(scopeDir))
16146
+ if (!existsSync13(scopeDir))
16182
16147
  return null;
16183
16148
  return readdirNames(scopeDir).sort();
16184
16149
  } catch {
@@ -16198,7 +16163,7 @@ function resolveStationProfilePackages(env = process.env) {
16198
16163
  const modulesDir = getBunGlobalModulesDir(env);
16199
16164
  let scopeDirs;
16200
16165
  try {
16201
- if (!existsSync14(modulesDir))
16166
+ if (!existsSync13(modulesDir))
16202
16167
  return null;
16203
16168
  scopeDirs = readdirNames(modulesDir).filter((name) => name.startsWith("@") && name.toLowerCase().includes("hasna"));
16204
16169
  } catch {
@@ -16273,10 +16238,10 @@ function refreshStationProfile(options = {}) {
16273
16238
  const path = getStationProfileCachePath(env);
16274
16239
  const generatedAt = new Date().toISOString();
16275
16240
  if (!options.dryRun) {
16276
- const existing = existsSync14(path) ? readFileSync13(path, "utf8") : null;
16241
+ const existing = existsSync13(path) ? readFileSync12(path, "utf8") : null;
16277
16242
  if (existing !== content) {
16278
- mkdirSync6(dirname9(path), { recursive: true });
16279
- writeFileSync5(path, content, "utf8");
16243
+ mkdirSync5(dirname8(path), { recursive: true });
16244
+ writeFileSync4(path, content, "utf8");
16280
16245
  }
16281
16246
  }
16282
16247
  return {
@@ -16292,9 +16257,9 @@ function refreshStationProfile(options = {}) {
16292
16257
  function readStationProfile(env = process.env) {
16293
16258
  const path = getStationProfileCachePath(env);
16294
16259
  try {
16295
- if (!existsSync14(path))
16260
+ if (!existsSync13(path))
16296
16261
  return null;
16297
- return readFileSync13(path, "utf8");
16262
+ return readFileSync12(path, "utf8");
16298
16263
  } catch {
16299
16264
  return null;
16300
16265
  }
@@ -16318,23 +16283,172 @@ function stationProfileSource(env = process.env) {
16318
16283
  init_instruction_graph();
16319
16284
  init_asset_plan();
16320
16285
 
16286
+ // src/lib/harness-discovery.ts
16287
+ import { accessSync, constants as constants2, lstatSync as lstatSync6, realpathSync as realpathSync4, statSync as statSync5 } from "fs";
16288
+ import { homedir as homedir10 } from "os";
16289
+ import { delimiter, dirname as dirname9, isAbsolute as isAbsolute7, join as join17, resolve as resolve11 } from "path";
16290
+ var HARNESS_DISCOVERY_SCHEMA = "hasna.instructions.harness-discovery/v1";
16291
+ var DISCOVERABLE_HARNESSES = ["claude", "codex", "opencode", "sumi"];
16292
+ function pathInput(value, home) {
16293
+ if (!value || value !== value.trim() || /[\x00-\x1f\x7f]/.test(value))
16294
+ throw new Error("Harness paths must be nonempty and contain no surrounding whitespace or control characters.");
16295
+ if (value.split("/").some((segment) => segment === "." || segment === ".."))
16296
+ throw new Error("Harness paths must not contain dot segments; normalizing them can change a symlink destination.");
16297
+ let path = value;
16298
+ if (home) {
16299
+ for (const prefix of ["~", "{{HOME}}", "{{HOME_DIR}}", "${HOME}"]) {
16300
+ if (path === prefix)
16301
+ path = home;
16302
+ else if (path.startsWith(`${prefix}/`))
16303
+ path = join17(home, path.slice(prefix.length + 1));
16304
+ }
16305
+ }
16306
+ if (!isAbsolute7(path))
16307
+ throw new Error("Harness paths must be absolute or explicitly relative to the owner home (~/ or {{HOME_DIR}}/).");
16308
+ return resolve11(path);
16309
+ }
16310
+ function observe(path) {
16311
+ let symlink = false;
16312
+ let viaSymlink = false;
16313
+ for (let parent = dirname9(path);parent !== dirname9(parent); parent = dirname9(parent)) {
16314
+ try {
16315
+ if (lstatSync6(parent).isSymbolicLink()) {
16316
+ viaSymlink = true;
16317
+ break;
16318
+ }
16319
+ } catch {}
16320
+ }
16321
+ try {
16322
+ symlink = lstatSync6(path).isSymbolicLink();
16323
+ const stat = statSync5(path);
16324
+ return { path, state: "present", realPath: realpathSync4(path), symlink, viaSymlink: viaSymlink || symlink, type: stat.isFile() ? "file" : stat.isDirectory() ? "directory" : "other" };
16325
+ } catch (error) {
16326
+ const missing = error.code === "ENOENT";
16327
+ return { path, state: missing ? symlink ? "dangling-symlink" : "missing" : "unreadable", realPath: null, symlink, viaSymlink: viaSymlink || symlink, type: null };
16328
+ }
16329
+ }
16330
+ function executableAt(path) {
16331
+ const observation = observe(path);
16332
+ if (observation.type !== "file")
16333
+ return null;
16334
+ try {
16335
+ accessSync(path, constants2.X_OK);
16336
+ return observation;
16337
+ } catch {
16338
+ return null;
16339
+ }
16340
+ }
16341
+ function configSelection(tool, home, env, override) {
16342
+ if (override !== undefined)
16343
+ return { path: pathInput(override, home), source: "override" };
16344
+ const key = { claude: "CLAUDE_CONFIG_DIR", codex: "CODEX_HOME", opencode: "OPENCODE_CONFIG_DIR", sumi: "SUMI_CONFIG_DIR" }[tool];
16345
+ const nativePath = (value, selector) => {
16346
+ try {
16347
+ return pathInput(value);
16348
+ } catch {
16349
+ throw new Error(`${selector} is present but is not an unambiguous absolute native path; supply the reviewed runtime path explicitly.`);
16350
+ }
16351
+ };
16352
+ if (env[key] !== undefined && !(tool === "codex" && env[key] === ""))
16353
+ return { path: nativePath(env[key], key), source: key };
16354
+ if (tool === "claude" || tool === "codex")
16355
+ return { path: join17(home, `.${tool}`), source: "native-default" };
16356
+ if (env.XDG_CONFIG_HOME !== undefined)
16357
+ return { path: join17(nativePath(env.XDG_CONFIG_HOME, "XDG_CONFIG_HOME"), tool), source: "XDG_CONFIG_HOME" };
16358
+ if (tool === "sumi") {
16359
+ if (env.SUMI_HOME !== undefined)
16360
+ return { path: join17(nativePath(env.SUMI_HOME, "SUMI_HOME"), "config"), source: "SUMI_HOME" };
16361
+ return null;
16362
+ }
16363
+ return { path: join17(home, ".config", tool), source: "native-default" };
16364
+ }
16365
+ function discoverHarnesses(options = {}) {
16366
+ const env = options.env ?? process.env;
16367
+ const home = pathInput(options.ownerHome ?? env.HOME ?? env.USERPROFILE ?? homedir10());
16368
+ const project = options.projectRoot === undefined ? null : observe(pathInput(options.projectRoot, home));
16369
+ const variables = { HOME_DIR: home };
16370
+ if (project)
16371
+ variables.PROJECT_ROOT = project.path;
16372
+ const tools = DISCOVERABLE_HARNESSES.map((tool) => {
16373
+ const diagnostics = [];
16374
+ const override = options.overrides?.[tool];
16375
+ let executable = null;
16376
+ let executableSource = null;
16377
+ if (override?.executable !== undefined) {
16378
+ executable = executableAt(pathInput(override.executable, home));
16379
+ executableSource = "override";
16380
+ if (!executable)
16381
+ diagnostics.push("Explicit executable is missing, unreadable, not a file, or not executable; PATH fallback is disabled.");
16382
+ } else {
16383
+ for (const directory of (env.PATH ?? "").split(delimiter)) {
16384
+ if (!isAbsolute7(directory)) {
16385
+ const warning = "Relative or empty PATH entries were ignored; this is an absolute-path candidate inventory, not proof of the launcher's selected executable.";
16386
+ if (!diagnostics.includes(warning))
16387
+ diagnostics.push(warning);
16388
+ continue;
16389
+ }
16390
+ let validated;
16391
+ try {
16392
+ validated = pathInput(directory);
16393
+ } catch {
16394
+ diagnostics.push("An ambiguous PATH directory was ignored; use an explicit executable override after reviewing its native path.");
16395
+ continue;
16396
+ }
16397
+ executable = executableAt(join17(validated, tool));
16398
+ if (executable) {
16399
+ executableSource = "PATH";
16400
+ break;
16401
+ }
16402
+ }
16403
+ }
16404
+ let selected = null;
16405
+ let configError = false;
16406
+ try {
16407
+ selected = configSelection(tool, home, env, override?.configDir);
16408
+ } catch (error) {
16409
+ if (override?.configDir !== undefined)
16410
+ throw error;
16411
+ configError = true;
16412
+ diagnostics.push(`Config root unresolved: ${error.message}`);
16413
+ }
16414
+ const config = selected ? { ...observe(selected.path), source: selected.source } : null;
16415
+ const filename = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
16416
+ const globalPrompt = config ? observe(join17(config.path, filename)) : null;
16417
+ const promptOverrides = tool === "codex" ? [...new Set([config?.path, project?.path].filter((path) => !!path))].map((path) => observe(join17(path, "AGENTS.override.md"))) : [];
16418
+ if (!config && !configError)
16419
+ diagnostics.push("Config root unresolved: supply the actual `sumi debug paths config` result as a configDir override; no runtime or migration was invoked.");
16420
+ if (executable) {
16421
+ variables[`${tool.toUpperCase()}_EXECUTABLE`] = executable.path;
16422
+ if (config)
16423
+ variables[`${tool.toUpperCase()}_CONFIG_DIR`] = config.path;
16424
+ }
16425
+ const linkedGlobal = !!globalPrompt?.viaSymlink || !!config?.viaSymlink;
16426
+ const hasOverride = promptOverrides.some((path) => path.state !== "missing" || path.viaSymlink);
16427
+ const scopeReviewRequired = linkedGlobal || hasOverride;
16428
+ if (linkedGlobal)
16429
+ diagnostics.push("Global config or prompt resolves through a symlink; preserve its lexical path and review the destination scope before planning writes.");
16430
+ if (hasOverride)
16431
+ diagnostics.push("A Codex AGENTS.override.md candidate may take precedence; review its native loader behavior and scope before applying AGENTS.md.");
16432
+ return { tool, status: executable ? "executable-found" : "absent", executable, executableSource, versionEvidence: "not-probed", config, globalPrompt, projectPrompt: project ? observe(join17(project.path, filename)) : null, promptOverrides, scopeReviewRequired, diagnostics };
16433
+ });
16434
+ return { schema: HARNESS_DISCOVERY_SCHEMA, ownerHome: observe(home), projectRoot: project, tools, templateVariables: variables };
16435
+ }
16321
16436
  // src/lib/session-apply.ts
16322
16437
  init_project_context();
16323
16438
  init_session_render_state();
16324
16439
  init_session_render();
16325
16440
  init_cursor_authority();
16326
16441
  init_session_authority();
16327
- import { createHash as createHash10, randomUUID as randomUUID4 } from "crypto";
16442
+ import { createHash as createHash9, randomUUID as randomUUID4 } from "crypto";
16328
16443
  import {
16329
- existsSync as existsSync15,
16330
- lstatSync as lstatSync6,
16331
- mkdirSync as mkdirSync7,
16332
- readFileSync as readFileSync14,
16444
+ existsSync as existsSync14,
16445
+ lstatSync as lstatSync7,
16446
+ mkdirSync as mkdirSync6,
16447
+ readFileSync as readFileSync13,
16333
16448
  readdirSync as readdirSync3,
16334
- statSync as statSync5
16449
+ statSync as statSync6
16335
16450
  } from "fs";
16336
- import { dirname as dirname10, isAbsolute as isAbsolute7, join as join17, parse as parse5, relative as relative6, resolve as resolve11 } from "path";
16337
-
16451
+ import { dirname as dirname10, isAbsolute as isAbsolute8, join as join18, parse as parse4, posix as posix3, relative as relative5, resolve as resolve12 } from "path";
16338
16452
  class SessionApplyError extends Error {
16339
16453
  constructor(message) {
16340
16454
  super(message);
@@ -16356,15 +16470,23 @@ function applySessionRenderUnlocked(plan, options, coordination) {
16356
16470
  const targetHome = assertSafeTargetHome(plan.targetHome);
16357
16471
  assertClaudeAuthorityStillClear(plan, targetHome, options.ownedClaudeAuthorities);
16358
16472
  const payloadFiles = [...plan.files, ...plan.assetFiles ?? []];
16359
- const files = [...payloadFiles, plan.manifestFile];
16360
16473
  const manifestPath = resolvePlannedFilePath(plan, plan.manifestFile, targetHome);
16474
+ assertManifestPrecondition(manifestPath, targetHome, options.expectedManifestSha256);
16361
16475
  const previousManifest = readPreviousManifest(manifestPath);
16362
16476
  const previousHashes = previousManifest ? new Map(previousManifest.files.map((file) => [file.relativePath, file.sha256])) : new Map;
16477
+ const adoptions = validateFileAdoptions(plan, targetHome, previousHashes, options);
16478
+ const adoptedHashes = new Map(adoptions.map((entry) => [entry.relativePath, entry.preimageSha256]));
16479
+ const reconciliations = validateFileReconciliations(plan, targetHome, previousManifest, options);
16480
+ const reconciledHashes = new Map(reconciliations.map((entry) => [entry.relativePath, entry.preimageSha256]));
16481
+ const retirements = validateFileRetirements(plan, targetHome, previousManifest, options);
16482
+ const retiredHashes = new Map(retirements.map((entry) => [entry.relativePath, entry.preimageSha256]));
16483
+ const manifestFile = manifestWithFileProvenance(plan, previousManifest, adoptions, reconciliations, retirements);
16484
+ const files = [...payloadFiles, manifestFile];
16363
16485
  const currentRelativePaths = new Set(files.map((file) => file.relativePath));
16364
16486
  const drift = checkSessionRenderDrift(targetHome, manifestPath);
16365
16487
  const results = [
16366
- ...files.map((file) => planFileResult(plan, file, targetHome, previousHashes, previousManifest, options)),
16367
- ...planStaleFileResults(plan, targetHome, previousManifest, currentRelativePaths, options)
16488
+ ...files.map((file) => planFileResult(plan, file, targetHome, previousHashes, previousManifest, options, adoptedHashes, reconciledHashes)),
16489
+ ...planStaleFileResults(plan, targetHome, previousManifest, currentRelativePaths, options, retiredHashes)
16368
16490
  ];
16369
16491
  assertNotSilentManagedWipeout(plan, results);
16370
16492
  const conflicts = results.filter((result) => result.action === "conflict");
@@ -16386,7 +16508,10 @@ function applySessionRenderUnlocked(plan, options, coordination) {
16386
16508
  skippedSources: plan.manifest.skippedSources,
16387
16509
  files: results,
16388
16510
  conflicts,
16389
- drift
16511
+ drift,
16512
+ adoptions,
16513
+ reconciliations,
16514
+ retirements
16390
16515
  };
16391
16516
  }
16392
16517
  let snapshotPath = null;
@@ -16399,10 +16524,16 @@ function applySessionRenderUnlocked(plan, options, coordination) {
16399
16524
  if (!options.dryRun) {
16400
16525
  const allowPortableFallback = coordination === null;
16401
16526
  const forcePortableFileOps = options.test_hooks?.force_portable_file_ops ?? false;
16527
+ assertManifestPrecondition(manifestPath, targetHome, options.expectedManifestSha256);
16402
16528
  ensureSessionTargetHome(targetHome);
16403
- rollback = writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps);
16529
+ rollback = writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps, adoptions, reconciliations, retirements);
16404
16530
  snapshotPath = rollback.snapshotPath;
16405
16531
  options.test_hooks?.before_apply_writes?.({ plan, results });
16532
+ assertManifestPrecondition(manifestPath, targetHome, options.expectedManifestSha256);
16533
+ for (const result of results) {
16534
+ assertExpectedSessionFileHash(result.path, targetHome, result.previousSha256);
16535
+ coordination?.assert_held();
16536
+ }
16406
16537
  const resultsByPath = new Map(results.map((result) => [result.path, result]));
16407
16538
  for (const file of payloadFiles) {
16408
16539
  applyPlannedFile(plan, file, targetHome, resultsByPath, coordination, allowPortableFallback, forcePortableFileOps);
@@ -16422,7 +16553,7 @@ function applySessionRenderUnlocked(plan, options, coordination) {
16422
16553
  });
16423
16554
  coordination?.assert_held();
16424
16555
  }
16425
- applyPlannedFile(plan, plan.manifestFile, targetHome, resultsByPath, coordination, allowPortableFallback, forcePortableFileOps);
16556
+ applyPlannedFile(plan, manifestFile, targetHome, resultsByPath, coordination, allowPortableFallback, forcePortableFileOps);
16426
16557
  }
16427
16558
  return {
16428
16559
  dryRun: options.dryRun ?? false,
@@ -16436,9 +16567,233 @@ function applySessionRenderUnlocked(plan, options, coordination) {
16436
16567
  skippedSources: plan.manifest.skippedSources,
16437
16568
  files: results,
16438
16569
  conflicts,
16439
- drift
16570
+ drift,
16571
+ adoptions,
16572
+ reconciliations,
16573
+ retirements
16440
16574
  };
16441
16575
  }
16576
+ function assertManifestPrecondition(path, targetHome, expected) {
16577
+ if (expected === undefined)
16578
+ return;
16579
+ if (!/^[a-f0-9]{64}$/.test(expected)) {
16580
+ throw new SessionApplyError("Expected session manifest SHA-256 must contain 64 lowercase hexadecimal characters.");
16581
+ }
16582
+ if (currentSessionFileHash(path, targetHome) !== expected) {
16583
+ throw new SessionApplyError("Session manifest SHA-256 precondition failed; reload the manifest and resolve a new plan.");
16584
+ }
16585
+ }
16586
+ function validateFileAdoptions(plan, targetHome, previousHashes, options) {
16587
+ const requests = options.adoptFiles ?? [];
16588
+ if (!Array.isArray(requests))
16589
+ throw new SessionApplyError("Session file adoptions must be an array.");
16590
+ if (requests.length > 0 && options.force) {
16591
+ throw new SessionApplyError("Exact file adoption cannot be combined with force.");
16592
+ }
16593
+ const seen = new Set;
16594
+ return requests.map((request) => {
16595
+ if (!request || typeof request.relativePath !== "string" || typeof request.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(request.sha256)) {
16596
+ throw new SessionApplyError("Each file adoption requires a plan-relative path and a 64-character lowercase SHA-256.");
16597
+ }
16598
+ if (seen.has(request.relativePath)) {
16599
+ throw new SessionApplyError(`Duplicate file adoption target: ${request.relativePath}`);
16600
+ }
16601
+ seen.add(request.relativePath);
16602
+ const candidates = exactPreimageCandidates(plan).filter((file2) => file2.relativePath === request.relativePath);
16603
+ if (candidates.length !== 1 || candidates[0].role === "manifest") {
16604
+ throw new SessionApplyError(`File adoption target is not a unique planned instruction output: ${request.relativePath}`);
16605
+ }
16606
+ const file = candidates[0];
16607
+ const path = resolvePlannedFilePath(plan, file, targetHome);
16608
+ if (previousHashes.has(file.relativePath) || sessionRenderOwnsPath(path)) {
16609
+ throw new SessionApplyError(`File adoption target is already managed: ${request.relativePath}`);
16610
+ }
16611
+ const observedSha256 = readExactFilePreimage(path, targetHome, request, "adoption");
16612
+ return {
16613
+ path,
16614
+ relativePath: file.relativePath,
16615
+ preimageSha256: observedSha256,
16616
+ renderedSha256: file.sha256,
16617
+ sourceIds: [...file.sourceIds]
16618
+ };
16619
+ });
16620
+ }
16621
+ function exactPreimageCandidates(plan) {
16622
+ const customAgentPaths = new Set((plan.assetPlan?.assets ?? []).filter((asset) => asset.kind === "custom-agent" && asset.support === "supported" && asset.action === "write" && asset.destination.strategy === "emit-file").map((asset) => posix3.normalize(asset.destination.relativePath)));
16623
+ return [...plan.files, ...(plan.assetFiles ?? []).filter((file) => customAgentPaths.has(file.relativePath))];
16624
+ }
16625
+ function readExactFilePreimage(path, targetHome, request, operation) {
16626
+ if (currentSessionFileHash(path, targetHome) === null) {
16627
+ throw new SessionApplyError(`File ${operation} target does not exist: ${request.relativePath}`);
16628
+ }
16629
+ const bytes = readFileSync13(path);
16630
+ if (!bytes.equals(Buffer.from(bytes.toString("utf8"), "utf8"))) {
16631
+ throw new SessionApplyError(`File ${operation} target must contain losslessly restorable UTF-8: ${request.relativePath}`);
16632
+ }
16633
+ const observedSha256 = createHash9("sha256").update(bytes).digest("hex");
16634
+ if (observedSha256 !== request.sha256) {
16635
+ throw new SessionApplyError(`File ${operation} SHA-256 precondition failed: ${request.relativePath}`);
16636
+ }
16637
+ return observedSha256;
16638
+ }
16639
+ function validateFileReconciliations(plan, targetHome, previousManifest, options) {
16640
+ const requests = options.reconcileFiles ?? [];
16641
+ if (!Array.isArray(requests))
16642
+ throw new SessionApplyError("Session file reconciliations must be an array.");
16643
+ if (requests.length === 0)
16644
+ return [];
16645
+ if (options.force)
16646
+ throw new SessionApplyError("Exact file reconciliation cannot be combined with force.");
16647
+ if (options.expectedManifestSha256 === undefined) {
16648
+ throw new SessionApplyError("Exact file reconciliation requires an expected manifest SHA-256 precondition.");
16649
+ }
16650
+ if (!previousManifest || previousManifest.targetOwner?.writer?.id !== SESSION_RENDERER_OWNER_ID || previousManifest.tool !== plan.tool || previousManifest.targetHome !== targetHome) {
16651
+ throw new SessionApplyError("File reconciliation requires this renderer's manifest for the same tool and target home.");
16652
+ }
16653
+ const seen = new Set;
16654
+ return requests.map((request) => {
16655
+ if (!request || typeof request.relativePath !== "string" || typeof request.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(request.sha256)) {
16656
+ throw new SessionApplyError("Each file reconciliation requires a plan-relative path and a 64-character lowercase SHA-256.");
16657
+ }
16658
+ if (seen.has(request.relativePath)) {
16659
+ throw new SessionApplyError(`Duplicate file reconciliation target: ${request.relativePath}`);
16660
+ }
16661
+ seen.add(request.relativePath);
16662
+ const candidates = exactPreimageCandidates(plan).filter((file2) => file2.relativePath === request.relativePath);
16663
+ if (candidates.length !== 1 || candidates[0].role === "manifest") {
16664
+ throw new SessionApplyError(`File reconciliation target is not a unique planned instruction output: ${request.relativePath}`);
16665
+ }
16666
+ const file = candidates[0];
16667
+ const path = resolvePlannedFilePath(plan, file, targetHome);
16668
+ const owned = previousManifest.files.filter((entry) => entry.relativePath === file.relativePath);
16669
+ if (owned.length !== 1 || owned[0].path !== path || owned[0].role !== file.role || !/^[a-f0-9]{64}$/.test(owned[0].sha256)) {
16670
+ throw new SessionApplyError(`File reconciliation target is not owned by the observed manifest: ${request.relativePath}`);
16671
+ }
16672
+ const observedSha256 = readExactFilePreimage(path, targetHome, request, "reconciliation");
16673
+ if (observedSha256 === owned[0].sha256) {
16674
+ throw new SessionApplyError(`File reconciliation target has no managed drift: ${request.relativePath}`);
16675
+ }
16676
+ if (observedSha256 === file.sha256) {
16677
+ throw new SessionApplyError(`File reconciliation target already matches the planned output: ${request.relativePath}`);
16678
+ }
16679
+ return {
16680
+ path,
16681
+ relativePath: file.relativePath,
16682
+ preimageSha256: observedSha256,
16683
+ renderedSha256: file.sha256,
16684
+ previousManagedSha256: owned[0].sha256,
16685
+ sourceIds: [...file.sourceIds]
16686
+ };
16687
+ });
16688
+ }
16689
+ function validateFileRetirements(plan, targetHome, previousManifest, options) {
16690
+ const requests = options.retireFiles ?? [];
16691
+ if (!Array.isArray(requests))
16692
+ throw new SessionApplyError("Session file retirements must be an array.");
16693
+ if (requests.length === 0)
16694
+ return [];
16695
+ if (options.force)
16696
+ throw new SessionApplyError("Exact file retirement cannot be combined with force.");
16697
+ if (options.expectedManifestSha256 === undefined) {
16698
+ throw new SessionApplyError("Exact file retirement requires an expected manifest SHA-256 precondition.");
16699
+ }
16700
+ if (!previousManifest || previousManifest.targetOwner?.writer?.id !== SESSION_RENDERER_OWNER_ID || previousManifest.tool !== plan.tool || previousManifest.targetHome !== targetHome) {
16701
+ throw new SessionApplyError("File retirement requires this renderer's manifest for the same tool and target home.");
16702
+ }
16703
+ const seen = new Set;
16704
+ return requests.map((request) => {
16705
+ if (!request || typeof request.relativePath !== "string" || typeof request.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(request.sha256)) {
16706
+ throw new SessionApplyError("Each file retirement requires a plan-relative path and a 64-character lowercase SHA-256.");
16707
+ }
16708
+ if (seen.has(request.relativePath))
16709
+ throw new SessionApplyError(`Duplicate file retirement target: ${request.relativePath}`);
16710
+ seen.add(request.relativePath);
16711
+ if (plan.allFiles.some((file) => file.relativePath === request.relativePath)) {
16712
+ throw new SessionApplyError(`File retirement target is retained by the new plan: ${request.relativePath}`);
16713
+ }
16714
+ const owned = previousManifest.files.filter((entry) => entry.relativePath === request.relativePath);
16715
+ const path = resolveManifestRelativePath(request.relativePath, targetHome);
16716
+ if (owned.length !== 1 || owned[0].path !== path || !/^[a-f0-9]{64}$/.test(owned[0].sha256) || !Array.isArray(owned[0].sourceIds) || owned[0].sourceIds.some((id) => typeof id !== "string") || !isPlanManagedFile(plan, request.relativePath, owned[0].role) && owned[0].role !== "asset") {
16717
+ throw new SessionApplyError(`File retirement target is not an obsolete managed fragment, rule, or asset: ${request.relativePath}`);
16718
+ }
16719
+ return {
16720
+ path,
16721
+ relativePath: request.relativePath,
16722
+ preimageSha256: readExactFilePreimage(path, targetHome, request, "retirement"),
16723
+ previousManagedSha256: owned[0].sha256,
16724
+ sourceIds: [...owned[0].sourceIds]
16725
+ };
16726
+ });
16727
+ }
16728
+ function manifestWithFileProvenance(plan, previousManifest, adoptions, reconciliations, retirements) {
16729
+ if (adoptions.length === 0 && previousManifest?.adoptions === undefined && reconciliations.length === 0 && previousManifest?.reconciliations === undefined && retirements.length === 0 && previousManifest?.retirements === undefined)
16730
+ return plan.manifestFile;
16731
+ if (previousManifest?.adoptions !== undefined && !Array.isArray(previousManifest.adoptions)) {
16732
+ throw new SessionApplyError("Previous session manifest adoption provenance is invalid.");
16733
+ }
16734
+ const entries = new Map;
16735
+ for (const entry of [
16736
+ ...previousManifest?.adoptions ?? [],
16737
+ ...adoptions.map(({ path: _path, ...receipt }) => receipt)
16738
+ ]) {
16739
+ if (!entry || typeof entry.relativePath !== "string" || !/^[a-f0-9]{64}$/.test(entry.preimageSha256) || !/^[a-f0-9]{64}$/.test(entry.renderedSha256) || !Array.isArray(entry.sourceIds) || entry.sourceIds.some((id) => typeof id !== "string")) {
16740
+ throw new SessionApplyError("Previous session manifest adoption provenance is invalid.");
16741
+ }
16742
+ resolveManifestRelativePath(entry.relativePath, plan.targetHome);
16743
+ const previous = entries.get(entry.relativePath);
16744
+ if (previous && JSON.stringify(previous) !== JSON.stringify(entry)) {
16745
+ throw new SessionApplyError(`Conflicting session file adoption provenance: ${entry.relativePath}`);
16746
+ }
16747
+ entries.set(entry.relativePath, entry);
16748
+ }
16749
+ if (previousManifest?.reconciliations !== undefined && !Array.isArray(previousManifest.reconciliations)) {
16750
+ throw new SessionApplyError("Previous session manifest reconciliation provenance is invalid.");
16751
+ }
16752
+ const reconciledEntries = new Map;
16753
+ for (const entry of [
16754
+ ...previousManifest?.reconciliations ?? [],
16755
+ ...reconciliations.map(({ path: _path, ...receipt }) => receipt)
16756
+ ]) {
16757
+ if (!entry || typeof entry.relativePath !== "string" || !/^[a-f0-9]{64}$/.test(entry.preimageSha256) || !/^[a-f0-9]{64}$/.test(entry.renderedSha256) || !/^[a-f0-9]{64}$/.test(entry.previousManagedSha256) || !Array.isArray(entry.sourceIds) || entry.sourceIds.some((id) => typeof id !== "string")) {
16758
+ throw new SessionApplyError("Previous session manifest reconciliation provenance is invalid.");
16759
+ }
16760
+ resolveManifestRelativePath(entry.relativePath, plan.targetHome);
16761
+ const canonical = {
16762
+ relativePath: entry.relativePath,
16763
+ preimageSha256: entry.preimageSha256,
16764
+ renderedSha256: entry.renderedSha256,
16765
+ previousManagedSha256: entry.previousManagedSha256,
16766
+ sourceIds: [...entry.sourceIds]
16767
+ };
16768
+ reconciledEntries.set(JSON.stringify(canonical), canonical);
16769
+ }
16770
+ if (previousManifest?.retirements !== undefined && !Array.isArray(previousManifest.retirements)) {
16771
+ throw new SessionApplyError("Previous session manifest retirement provenance is invalid.");
16772
+ }
16773
+ const retiredEntries = new Map;
16774
+ for (const entry of [...previousManifest?.retirements ?? [], ...retirements.map(({ path: _path, ...receipt }) => receipt)]) {
16775
+ if (!entry || typeof entry.relativePath !== "string" || !/^[a-f0-9]{64}$/.test(entry.preimageSha256) || !/^[a-f0-9]{64}$/.test(entry.previousManagedSha256) || !Array.isArray(entry.sourceIds) || entry.sourceIds.some((id) => typeof id !== "string")) {
16776
+ throw new SessionApplyError("Previous session manifest retirement provenance is invalid.");
16777
+ }
16778
+ resolveManifestRelativePath(entry.relativePath, plan.targetHome);
16779
+ const canonical = {
16780
+ relativePath: entry.relativePath,
16781
+ preimageSha256: entry.preimageSha256,
16782
+ previousManagedSha256: entry.previousManagedSha256,
16783
+ sourceIds: [...entry.sourceIds]
16784
+ };
16785
+ retiredEntries.set(JSON.stringify(canonical), canonical);
16786
+ }
16787
+ const manifest = {
16788
+ ...plan.manifest,
16789
+ ...adoptions.length > 0 || previousManifest?.adoptions !== undefined ? { adoptions: [...entries.values()] } : {},
16790
+ ...reconciliations.length > 0 || previousManifest?.reconciliations !== undefined ? { reconciliations: [...reconciledEntries.values()] } : {},
16791
+ ...retirements.length > 0 || previousManifest?.retirements !== undefined ? { retirements: [...retiredEntries.values()] } : {}
16792
+ };
16793
+ const content = `${JSON.stringify(manifest, null, 2)}
16794
+ `;
16795
+ return { ...plan.manifestFile, content, sha256: sha2569(content) };
16796
+ }
16442
16797
  function assertCursorAuthorityUnchanged(plan) {
16443
16798
  if (plan.tool !== "cursor" || plan.targetKind === "blocked")
16444
16799
  return;
@@ -16465,13 +16820,13 @@ function assertClaudeAuthorityStillClear(plan, targetHome, ownedClaudeAuthoritie
16465
16820
  throw new SessionApplyError(`Claude authority changed after planning; refusing to apply: ${summary}`);
16466
16821
  }
16467
16822
  function ensureSessionTargetHome(targetHome) {
16468
- if (!existsSync15(targetHome))
16469
- mkdirSync7(targetHome, { recursive: true, mode: 448 });
16823
+ if (!existsSync14(targetHome))
16824
+ mkdirSync6(targetHome, { recursive: true, mode: 448 });
16470
16825
  assertSafeTargetHome(targetHome);
16471
16826
  }
16472
16827
  function checkSessionRenderDrift(targetHome, manifestPath) {
16473
16828
  const safeTargetHome = assertSafeTargetHome(targetHome);
16474
- const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative6(safeTargetHome, resolve11(manifestPath)), safeTargetHome) : resolve11(safeTargetHome, ".hasna", "session-render-manifest.json");
16829
+ const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve12(manifestPath)), safeTargetHome) : resolve12(safeTargetHome, ".hasna", "session-render-manifest.json");
16475
16830
  const checkedAt = new Date().toISOString();
16476
16831
  const previousManifest = readPreviousManifest(resolvedManifestPath);
16477
16832
  if (!previousManifest) {
@@ -16488,7 +16843,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
16488
16843
  const drifted = [];
16489
16844
  for (const file of previousManifest.files) {
16490
16845
  const target = resolveManifestRelativePath(file.relativePath, safeTargetHome);
16491
- if (!existsSync15(target)) {
16846
+ if (!existsSync14(target)) {
16492
16847
  missing.push({
16493
16848
  path: target,
16494
16849
  relativePath: file.relativePath,
@@ -16498,7 +16853,7 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
16498
16853
  });
16499
16854
  continue;
16500
16855
  }
16501
- const actualSha256 = sha25610(readFileSync14(target, "utf-8"));
16856
+ const actualSha256 = sha2569(readFileSync13(target, "utf-8"));
16502
16857
  if (actualSha256 !== file.sha256) {
16503
16858
  drifted.push({
16504
16859
  path: target,
@@ -16521,13 +16876,13 @@ function checkSessionRenderDrift(targetHome, manifestPath) {
16521
16876
  function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
16522
16877
  const snapshot = readSessionRenderSnapshot(snapshotPath);
16523
16878
  const targetHome = assertSafeTargetHome(snapshot.targetHome);
16524
- const resolvedSnapshotPath = resolve11(snapshotPath);
16879
+ const resolvedSnapshotPath = resolve12(snapshotPath);
16525
16880
  const snapshotDir = getSessionRenderSnapshotDir(targetHome);
16526
- const snapshotDirRelative = relative6(snapshotDir, resolvedSnapshotPath);
16527
- const insideSnapshotDir = snapshotDirRelative !== ".." && !snapshotDirRelative.startsWith("../") && !isAbsolute7(snapshotDirRelative);
16881
+ const snapshotDirRelative = relative5(snapshotDir, resolvedSnapshotPath);
16882
+ const insideSnapshotDir = snapshotDirRelative !== ".." && !snapshotDirRelative.startsWith("../") && !isAbsolute8(snapshotDirRelative);
16528
16883
  if (!insideSnapshotDir) {
16529
- const snapshotRelativePath = relative6(targetHome, resolvedSnapshotPath);
16530
- if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute7(snapshotRelativePath)) {
16884
+ const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
16885
+ if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute8(snapshotRelativePath)) {
16531
16886
  throw new SessionApplyError("Session snapshot must be stored inside its session-render snapshot location.");
16532
16887
  }
16533
16888
  }
@@ -16644,19 +16999,19 @@ function requiredRestoreHash(file) {
16644
16999
  return file.previousSha256;
16645
17000
  }
16646
17001
  function readSessionRenderSnapshot(snapshotPath) {
16647
- const resolved = resolve11(snapshotPath);
16648
- if (!existsSync15(resolved))
17002
+ const resolved = resolve12(snapshotPath);
17003
+ if (!existsSync14(resolved))
16649
17004
  throw new SessionApplyError(`Session snapshot not found: ${snapshotPath}`);
16650
- const stat = lstatSync6(resolved);
17005
+ const stat = lstatSync7(resolved);
16651
17006
  if (stat.isSymbolicLink() || !stat.isFile()) {
16652
17007
  throw new SessionApplyError(`Session snapshot is not a regular file: ${snapshotPath}`);
16653
17008
  }
16654
- if (statSync5(resolved).size > 32 * 1024 * 1024) {
17009
+ if (statSync6(resolved).size > 32 * 1024 * 1024) {
16655
17010
  throw new SessionApplyError(`Session snapshot exceeds the 32 MiB restore limit: ${snapshotPath}`);
16656
17011
  }
16657
17012
  let parsed;
16658
17013
  try {
16659
- parsed = JSON.parse(readFileSync14(resolved, "utf8"));
17014
+ parsed = JSON.parse(readFileSync13(resolved, "utf8"));
16660
17015
  } catch {
16661
17016
  throw new SessionApplyError(`Session snapshot is not valid JSON: ${snapshotPath}`);
16662
17017
  }
@@ -16678,7 +17033,7 @@ function readSessionRenderSnapshot(snapshotPath) {
16678
17033
  const previousManifest = snapshot.previousManifest;
16679
17034
  const previousFiles = new Map;
16680
17035
  for (const file of snapshot.files) {
16681
- if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" || sha25610(file.content) !== file.sha256) {
17036
+ if (!file || typeof file.relativePath !== "string" || typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.content !== "string" || sha2569(file.content) !== file.sha256) {
16682
17037
  throw new SessionApplyError(`Session snapshot previous file metadata is invalid: ${snapshotPath}`);
16683
17038
  }
16684
17039
  resolveSnapshotFilePath(file.relativePath, file.path, targetHome);
@@ -16725,8 +17080,8 @@ function readSessionRenderSnapshot(snapshotPath) {
16725
17080
  }
16726
17081
  function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
16727
17082
  assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
16728
- const manifestPath = resolve11(snapshot.manifestPath);
16729
- const manifestRelativePath = relative6(targetHome, manifestPath).replaceAll("\\", "/");
17083
+ const manifestPath = resolve12(snapshot.manifestPath);
17084
+ const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
16730
17085
  resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
16731
17086
  const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
16732
17087
  if (manifestSha256 === null) {
@@ -16734,7 +17089,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
16734
17089
  }
16735
17090
  let parsedManifest;
16736
17091
  try {
16737
- parsedManifest = JSON.parse(readFileSync14(manifestPath, "utf8"));
17092
+ parsedManifest = JSON.parse(readFileSync13(manifestPath, "utf8"));
16738
17093
  } catch {
16739
17094
  throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is not valid JSON: ${snapshotPath}`);
16740
17095
  }
@@ -16742,7 +17097,7 @@ function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previou
16742
17097
  throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest is invalid: ${snapshotPath}`);
16743
17098
  }
16744
17099
  const appliedManifest = parsedManifest;
16745
- if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" || resolve11(appliedManifest.targetHome) !== targetHome || appliedManifest.targetKind !== "session-home" && appliedManifest.targetKind !== "project-root" || !Array.isArray(appliedManifest.files)) {
17100
+ if (appliedManifest.schema !== SESSION_RENDER_SCHEMA || appliedManifest.tool !== snapshot.tool || appliedManifest.profile !== snapshot.profile || typeof appliedManifest.targetHome !== "string" || resolve12(appliedManifest.targetHome) !== targetHome || appliedManifest.targetKind !== "session-home" && appliedManifest.targetKind !== "project-root" || !Array.isArray(appliedManifest.files)) {
16746
17101
  throw new SessionApplyError(`Pre-rollback legacy v1 applied manifest does not match its snapshot: ${snapshotPath}`);
16747
17102
  }
16748
17103
  const afterFiles = [];
@@ -16827,16 +17182,16 @@ function assertNoNewerSessionSnapshot(snapshotPath, createdAt, targetHome) {
16827
17182
  throw new SessionApplyError(`Pre-rollback legacy v1 snapshot has an invalid creation time: ${snapshotPath}`);
16828
17183
  }
16829
17184
  for (const entry of readdirSync3(dirname10(snapshotPath))) {
16830
- const candidatePath = resolve11(dirname10(snapshotPath), entry);
16831
- if (candidatePath === resolve11(snapshotPath) || !entry.endsWith(".json"))
17185
+ const candidatePath = resolve12(dirname10(snapshotPath), entry);
17186
+ if (candidatePath === resolve12(snapshotPath) || !entry.endsWith(".json"))
16832
17187
  continue;
16833
- const candidateStat = lstatSync6(candidatePath);
17188
+ const candidateStat = lstatSync7(candidatePath);
16834
17189
  if (candidateStat.isSymbolicLink() || !candidateStat.isFile() || candidateStat.size > 32 * 1024 * 1024)
16835
17190
  continue;
16836
17191
  try {
16837
- const candidate = JSON.parse(readFileSync14(candidatePath, "utf8"));
17192
+ const candidate = JSON.parse(readFileSync13(candidatePath, "utf8"));
16838
17193
  const candidateCreatedAtMs = typeof candidate.createdAt === "string" ? Date.parse(candidate.createdAt) : Number.NaN;
16839
- if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve11(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
17194
+ if ((candidate.schema === "hasna.configs.session-render-snapshot/v1" || candidate.schema === "hasna.configs.session-render-snapshot/v2") && typeof candidate.targetHome === "string" && resolve12(candidate.targetHome) === targetHome && Number.isFinite(candidateCreatedAtMs) && candidateCreatedAtMs >= createdAtMs) {
16840
17195
  throw new SessionApplyError(`Cannot restore pre-rollback legacy v1 snapshot after a newer session snapshot exists: ${candidatePath}`);
16841
17196
  }
16842
17197
  } catch (error) {
@@ -16894,7 +17249,7 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
16894
17249
  return "create";
16895
17250
  }
16896
17251
  if (file.role === "manifest" && previousManifest) {
16897
- const previousManifestSha256 = sha25610(`${JSON.stringify(previousManifest, null, 2)}
17252
+ const previousManifestSha256 = sha2569(`${JSON.stringify(previousManifest, null, 2)}
16898
17253
  `);
16899
17254
  if (previousManifestSha256 !== file.sha256) {
16900
17255
  throw new SessionApplyError(`Cannot infer legacy v1 manifest action without a before-image: ${file.relativePath}`);
@@ -16905,17 +17260,34 @@ function inferLegacySnapshotAction(file, previousFiles, previousManifestFiles, p
16905
17260
  }
16906
17261
  function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
16907
17262
  const path = resolveManifestRelativePath(relativePath, targetHome);
16908
- if (resolve11(recordedPath) !== path) {
17263
+ if (resolve12(recordedPath) !== path) {
16909
17264
  throw new SessionApplyError(`Session snapshot file path mismatch for ${relativePath}`);
16910
17265
  }
16911
17266
  return path;
16912
17267
  }
16913
- function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
17268
+ function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options, adoptedHashes, reconciledHashes) {
16914
17269
  const target = resolvePlannedFilePath(plan, file, targetHome);
16915
- const previousContent = existsSync15(target) ? readFileSync14(target, "utf-8") : null;
16916
- const previousSha256 = previousContent === null ? null : sha25610(previousContent);
17270
+ const previousContent = existsSync14(target) ? readFileSync13(target, "utf-8") : null;
17271
+ const previousSha256 = previousContent === null ? null : sha2569(previousContent);
16917
17272
  const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
16918
17273
  const changed = previousContent !== file.content;
17274
+ const exactSha256 = adoptedHashes.get(file.relativePath) ?? reconciledHashes.get(file.relativePath);
17275
+ if (exactSha256 !== undefined) {
17276
+ const operation = adoptedHashes.has(file.relativePath) ? "adoption" : "reconciliation";
17277
+ if (previousSha256 !== exactSha256) {
17278
+ throw new SessionApplyError(`File ${operation} SHA-256 precondition failed: ${file.relativePath}`);
17279
+ }
17280
+ return {
17281
+ path: target,
17282
+ relativePath: file.relativePath,
17283
+ role: file.role,
17284
+ action: "update",
17285
+ changed,
17286
+ previousSha256,
17287
+ newSha256: file.sha256,
17288
+ reason: `exact observed SHA-256 ${operation}`
17289
+ };
17290
+ }
16919
17291
  if (previousContent !== null && !options.force && !previouslyManaged) {
16920
17292
  return {
16921
17293
  path: target,
@@ -17004,17 +17376,55 @@ function isPlanManagedFile(plan, relativePath, role) {
17004
17376
  return true;
17005
17377
  return plan.tool === "claude" && role === "rule" && relativePath.startsWith("rules/");
17006
17378
  }
17007
- function planStaleFileResults(plan, targetHome, previousManifest, currentRelativePaths, options) {
17379
+ function planStaleFileResults(plan, targetHome, previousManifest, currentRelativePaths, options, retiredHashes) {
17008
17380
  if (!previousManifest)
17009
17381
  return [];
17010
- return previousManifest.files.filter((file) => !currentRelativePaths.has(file.relativePath)).filter((file) => isPlanManagedFile(plan, file.relativePath, file.role)).map((file) => planStaleFileResult(file, targetHome, options)).filter((result) => result !== null);
17382
+ return previousManifest.files.filter((file) => !currentRelativePaths.has(file.relativePath)).filter((file) => isPlanManagedFile(plan, file.relativePath, file.role) || file.role === "asset" || retiredHashes.has(file.relativePath)).map((file) => planStaleFileResult(file, targetHome, options, retiredHashes.get(file.relativePath))).filter((result) => result !== null);
17011
17383
  }
17012
- function planStaleFileResult(file, targetHome, options) {
17384
+ function planStaleFileResult(file, targetHome, options, retiredHash) {
17013
17385
  const target = resolveManifestRelativePath(file.relativePath, targetHome);
17014
- if (!existsSync15(target))
17015
- return null;
17016
- const previousContent = readFileSync14(target, "utf-8");
17017
- const previousSha256 = sha25610(previousContent);
17386
+ if (!existsSync14(target)) {
17387
+ if (file.role !== "asset")
17388
+ return null;
17389
+ return {
17390
+ path: target,
17391
+ relativePath: file.relativePath,
17392
+ role: file.role,
17393
+ action: "conflict",
17394
+ changed: true,
17395
+ previousSha256: null,
17396
+ newSha256: "",
17397
+ reason: "obsolete managed asset is missing; preserve manifest ownership until its reviewed preimage is restored and retired exactly"
17398
+ };
17399
+ }
17400
+ const previousContent = readFileSync13(target, "utf-8");
17401
+ const previousSha256 = sha2569(previousContent);
17402
+ if (retiredHash !== undefined) {
17403
+ if (previousSha256 !== retiredHash)
17404
+ throw new SessionApplyError(`File retirement preimage changed after validation: ${file.relativePath}`);
17405
+ return {
17406
+ path: target,
17407
+ relativePath: file.relativePath,
17408
+ role: file.role,
17409
+ action: "delete",
17410
+ changed: true,
17411
+ previousSha256,
17412
+ newSha256: "",
17413
+ reason: "exact reviewed obsolete managed file retired"
17414
+ };
17415
+ }
17416
+ if (file.role === "asset") {
17417
+ return {
17418
+ path: target,
17419
+ relativePath: file.relativePath,
17420
+ role: file.role,
17421
+ action: "conflict",
17422
+ changed: true,
17423
+ previousSha256,
17424
+ newSha256: "",
17425
+ reason: "obsolete managed asset requires exact retirement; preserve ownership until --retire-file and --expected-manifest-sha256 are supplied"
17426
+ };
17427
+ }
17018
17428
  if (!options.force && previousSha256 !== file.sha256) {
17019
17429
  return {
17020
17430
  path: target,
@@ -17058,31 +17468,31 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
17058
17468
  return previousHashes.get(file.relativePath) === previousSha256;
17059
17469
  }
17060
17470
  function resolvePlannedFilePath(plan, file, targetHome) {
17061
- const target = resolve11(targetHome, ...file.relativePath.split("/"));
17062
- const rel = relative6(targetHome, target);
17063
- if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute7(rel)) {
17471
+ const target = resolve12(targetHome, ...file.relativePath.split("/"));
17472
+ const rel = relative5(targetHome, target);
17473
+ if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute8(rel)) {
17064
17474
  throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
17065
17475
  }
17066
- if (resolve11(file.path) !== target) {
17476
+ if (resolve12(file.path) !== target) {
17067
17477
  throw new SessionApplyError(`Session file path mismatch for ${file.relativePath}: ${file.path}`);
17068
17478
  }
17069
17479
  assertNoSymlinkSegments2(targetHome, target);
17070
17480
  return target;
17071
17481
  }
17072
17482
  function resolveManifestRelativePath(relativePath, targetHome) {
17073
- const target = resolve11(targetHome, ...relativePath.split(/[\\/]+/));
17074
- const rel = relative6(targetHome, target);
17075
- if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute7(rel)) {
17483
+ const target = resolve12(targetHome, ...relativePath.split(/[\\/]+/));
17484
+ const rel = relative5(targetHome, target);
17485
+ if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute8(rel)) {
17076
17486
  throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
17077
17487
  }
17078
17488
  assertNoSymlinkSegments2(targetHome, target);
17079
17489
  return target;
17080
17490
  }
17081
17491
  function readPreviousManifest(path) {
17082
- if (!existsSync15(path))
17492
+ if (!existsSync14(path))
17083
17493
  return null;
17084
17494
  try {
17085
- const parsed = JSON.parse(readFileSync14(path, "utf-8"));
17495
+ const parsed = JSON.parse(readFileSync13(path, "utf-8"));
17086
17496
  if (parsed.schema !== SESSION_RENDER_SCHEMA)
17087
17497
  return null;
17088
17498
  if (!Array.isArray(parsed.files))
@@ -17116,18 +17526,18 @@ function applyPlannedFile(plan, file, targetHome, resultsByPath, coordination, a
17116
17526
  function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
17117
17527
  const actualHash = currentSessionFileHash(path, targetHome);
17118
17528
  if (actualHash !== expectedHash) {
17119
- throw new SessionApplyError(`Session apply path changed after planning: ${relative6(targetHome, path)}`);
17529
+ throw new SessionApplyError(`Session apply path changed after planning: ${relative5(targetHome, path)}`);
17120
17530
  }
17121
17531
  }
17122
17532
  function currentSessionFileHash(path, targetHome) {
17123
17533
  assertNoSymlinkSegments2(targetHome, path);
17124
- if (!existsSync15(path))
17534
+ if (!existsSync14(path))
17125
17535
  return null;
17126
- const stat = lstatSync6(path);
17536
+ const stat = lstatSync7(path);
17127
17537
  if (stat.isSymbolicLink() || !stat.isFile()) {
17128
17538
  throw new SessionApplyError(`Session apply path is not a regular file: ${path}`);
17129
17539
  }
17130
- return sha25610(readFileSync14(path, "utf-8"));
17540
+ return sha2569(readFileSync13(path, "utf-8"));
17131
17541
  }
17132
17542
  function requiredPreviousHash(result) {
17133
17543
  if (result.previousSha256 === null) {
@@ -17135,14 +17545,18 @@ function requiredPreviousHash(result) {
17135
17545
  }
17136
17546
  return result.previousSha256;
17137
17547
  }
17138
- function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
17139
- const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").filter((result) => existsSync15(result.path)).map((result) => {
17140
- const content = readFileSync14(result.path, "utf-8");
17548
+ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps, adoptions, reconciliations, retirements) {
17549
+ const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").map((result) => {
17550
+ assertExpectedSessionFileHash(result.path, targetHome, result.previousSha256);
17551
+ const content = readFileSync13(result.path, "utf-8");
17552
+ if (sha2569(content) !== result.previousSha256) {
17553
+ throw new SessionApplyError(`Session snapshot preimage changed after planning: ${result.relativePath}`);
17554
+ }
17141
17555
  return {
17142
17556
  path: result.path,
17143
17557
  relativePath: result.relativePath,
17144
17558
  role: result.role,
17145
- sha256: sha25610(content),
17559
+ sha256: sha2569(content),
17146
17560
  content
17147
17561
  };
17148
17562
  });
@@ -17155,7 +17569,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
17155
17569
  };
17156
17570
  }
17157
17571
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
17158
- const snapshotPath = join17(getSessionRenderSnapshotDir(targetHome), `${timestamp}-${randomUUID4()}.json`);
17572
+ const snapshotPath = join18(getSessionRenderSnapshotDir(targetHome), `${timestamp}-${randomUUID4()}.json`);
17159
17573
  const afterFiles = results.map((result) => {
17160
17574
  if (result.action === "conflict") {
17161
17575
  throw new SessionApplyError(`Cannot snapshot unresolved conflict: ${result.relativePath}`);
@@ -17178,13 +17592,20 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
17178
17592
  manifestPath,
17179
17593
  previousManifest,
17180
17594
  files: existingFiles,
17181
- afterFiles
17595
+ afterFiles,
17596
+ ...adoptions.length > 0 ? { adoptions } : {},
17597
+ ...reconciliations.length > 0 ? { reconciliations } : {},
17598
+ ...retirements.length > 0 ? { retirements } : {}
17182
17599
  };
17600
+ const snapshotContent = `${JSON.stringify(snapshot, null, 2)}
17601
+ `;
17602
+ if ((adoptions.length > 0 || reconciliations.length > 0 || retirements.length > 0) && Buffer.byteLength(snapshotContent, "utf8") > 32 * 1024 * 1024) {
17603
+ throw new SessionApplyError("Exact file preimage snapshot exceeds the 32 MiB restore limit.");
17604
+ }
17183
17605
  coordination?.assert_held();
17184
17606
  writeProjectContextCoordinatedFile({
17185
17607
  path: snapshotPath,
17186
- content: `${JSON.stringify(snapshot, null, 2)}
17187
- `,
17608
+ content: snapshotContent,
17188
17609
  workspace_root: sessionRenderSnapshotWorkspaceRoot(targetHome),
17189
17610
  default_mode: 384,
17190
17611
  expected_hash: null,
@@ -17201,45 +17622,262 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
17201
17622
  };
17202
17623
  }
17203
17624
  function assertSafeTargetHome(targetHome) {
17204
- if (!isAbsolute7(targetHome))
17625
+ if (!isAbsolute8(targetHome))
17205
17626
  throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
17206
- const normalized = resolve11(targetHome);
17207
- if (normalized === parse5(normalized).root) {
17627
+ const normalized = resolve12(targetHome);
17628
+ if (normalized === parse4(normalized).root) {
17208
17629
  throw new SessionApplyError(`Session target home cannot be the filesystem root: ${targetHome}`);
17209
17630
  }
17210
- assertNoSymlinkAncestors3(normalized);
17211
- if (existsSync15(normalized) && lstatSync6(normalized).isSymbolicLink()) {
17631
+ assertNoSymlinkAncestors2(normalized);
17632
+ if (existsSync14(normalized) && lstatSync7(normalized).isSymbolicLink()) {
17212
17633
  throw new SessionApplyError(`Session target home cannot be a symlink: ${normalized}`);
17213
17634
  }
17214
17635
  return normalized;
17215
17636
  }
17216
17637
  function assertNoSymlinkSegments2(root, target) {
17217
- assertNoSymlinkAncestors3(root);
17218
- const rel = relative6(root, target);
17638
+ assertNoSymlinkAncestors2(root);
17639
+ const rel = relative5(root, target);
17219
17640
  let current = root;
17220
17641
  for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
17221
- current = join17(current, segment);
17222
- if (existsSync15(current) && lstatSync6(current).isSymbolicLink()) {
17642
+ current = join18(current, segment);
17643
+ if (existsSync14(current) && lstatSync7(current).isSymbolicLink()) {
17223
17644
  throw new SessionApplyError(`Session apply path uses a symlink: ${current}`);
17224
17645
  }
17225
17646
  }
17226
17647
  }
17227
- function assertNoSymlinkAncestors3(path) {
17228
- const normalized = resolve11(path);
17229
- const parsed = parse5(normalized);
17648
+ function assertNoSymlinkAncestors2(path) {
17649
+ const normalized = resolve12(path);
17650
+ const parsed = parse4(normalized);
17230
17651
  let current = parsed.root;
17231
- const rel = relative6(parsed.root, normalized);
17652
+ const rel = relative5(parsed.root, normalized);
17232
17653
  for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
17233
- current = join17(current, segment);
17234
- if (!existsSync15(current))
17654
+ current = join18(current, segment);
17655
+ if (!existsSync14(current))
17235
17656
  return;
17236
- if (lstatSync6(current).isSymbolicLink()) {
17657
+ if (lstatSync7(current).isSymbolicLink()) {
17237
17658
  throw new SessionApplyError(`Session apply path uses a symlink ancestor: ${current}`);
17238
17659
  }
17239
17660
  }
17240
17661
  }
17241
- function sha25610(content) {
17242
- return createHash10("sha256").update(content).digest("hex");
17662
+ function sha2569(content) {
17663
+ return createHash9("sha256").update(content).digest("hex");
17664
+ }
17665
+ // src/lib/session-refresh.ts
17666
+ init_zod();
17667
+ import { createHash as createHash10 } from "crypto";
17668
+ import { closeSync as closeSync4, constants as constants3, fstatSync as fstatSync4, lstatSync as lstatSync8, openSync as openSync4, readFileSync as readFileSync14 } from "fs";
17669
+ import { basename as basename6, dirname as dirname11, isAbsolute as isAbsolute9, join as join19, parse as parse5, resolve as resolve13 } from "path";
17670
+
17671
+ // src/lib/global-source-coverage.ts
17672
+ var GLOBAL_SOURCE_SLUG_PREFIX = "global-";
17673
+ function expectedGlobalSourceSlugs(registryConfigs) {
17674
+ return registryConfigs.filter((c) => c.slug.startsWith(GLOBAL_SOURCE_SLUG_PREFIX)).filter((c) => instructionSourceRejection(c) === null).map((c) => c.slug).sort();
17675
+ }
17676
+ function accountedGlobalSourceSlugs(manifest) {
17677
+ return [...manifest.sources, ...manifest.skippedSources].map((source) => source.id).filter((id) => id.startsWith(GLOBAL_SOURCE_SLUG_PREFIX));
17678
+ }
17679
+ function computeGlobalSourceCoverage(registryConfigs, configuredSlugs) {
17680
+ const expected = expectedGlobalSourceSlugs(registryConfigs);
17681
+ const expectedSet = new Set(expected);
17682
+ const configuredSet = new Set(configuredSlugs);
17683
+ const missing = expected.filter((slug2) => !configuredSet.has(slug2));
17684
+ const unexpected = [...configuredSet].filter((slug2) => slug2.startsWith(GLOBAL_SOURCE_SLUG_PREFIX) && !expectedSet.has(slug2)).sort();
17685
+ return {
17686
+ expectedSlugs: expected,
17687
+ excludedSources: registryConfigs.filter((config) => config.slug.startsWith(GLOBAL_SOURCE_SLUG_PREFIX)).flatMap((config) => {
17688
+ const reason = instructionSourceRejection(config);
17689
+ return reason ? [{ slug: config.slug, reason }] : [];
17690
+ }).sort((left, right) => left.slug.localeCompare(right.slug)),
17691
+ configuredSlugs: [...configuredSet].sort(),
17692
+ missingSlugs: missing,
17693
+ unexpectedSlugs: unexpected,
17694
+ complete: missing.length === 0
17695
+ };
17696
+ }
17697
+
17698
+ // src/lib/session-refresh.ts
17699
+ init_instruction_graph();
17700
+ init_project_context();
17701
+ init_session_render();
17702
+ var nonempty = exports_external.string().min(1).max(4096);
17703
+ var selectorSchema = exports_external.object({
17704
+ schema: exports_external.literal("hasna.instructions.hosted-profile-selector/v1"),
17705
+ authority: nonempty,
17706
+ profileId: nonempty,
17707
+ providerVersion: nonempty,
17708
+ providerVariant: nonempty.optional(),
17709
+ model: nonempty.optional(),
17710
+ path: nonempty.optional(),
17711
+ manual: exports_external.array(nonempty).max(256),
17712
+ codewithNativeImports: exports_external.boolean(),
17713
+ allowEmptySources: exports_external.boolean(),
17714
+ stationProfile: exports_external.boolean(),
17715
+ checkGlobalCoverage: exports_external.boolean(),
17716
+ assetSurface: nonempty.optional(),
17717
+ assetScope: exports_external.enum(["global", "project", "session"]).optional()
17718
+ }).strict();
17719
+ function authority(value) {
17720
+ const url = new URL(value);
17721
+ if (url.username || url.password || url.search || url.hash || !["https:", "http:"].includes(url.protocol)) {
17722
+ throw new Error("SESSION_REFRESH_AUTHORITY_INVALID: expected a credential-free Instructions API authority.");
17723
+ }
17724
+ return url.toString().replace(/\/$/, "");
17725
+ }
17726
+ function normalizeSessionHostedProfileSelector(value) {
17727
+ const parsed = selectorSchema.parse(value);
17728
+ return { ...parsed, authority: authority(parsed.authority) };
17729
+ }
17730
+ function assertHostedProfileBindings(profileId, configs, bindings) {
17731
+ const configIds = new Set(configs.map((config) => config.id));
17732
+ const bindingIds = new Set(bindings.map((binding) => binding.config_id));
17733
+ if (configIds.size !== configs.length || bindingIds.size !== bindings.length || bindings.some((binding) => binding.profile_id !== profileId || !configIds.has(binding.config_id)) || [...configIds].some((id) => !bindingIds.has(id))) {
17734
+ throw new Error("HOSTED_PROFILE_BINDINGS_INCOMPLETE: exact one-to-one config membership and explicit profile bindings are required; refusing legacy activation fallback.");
17735
+ }
17736
+ for (const row of bindings) {
17737
+ const raw = row.binding;
17738
+ const invalid = () => new Error("HOSTED_PROFILE_BINDINGS_INVALID: each hosted binding must contain a complete explicit schema, activation, required flag and fallback; refusing legacy activation defaults.");
17739
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
17740
+ throw invalid();
17741
+ const record = raw;
17742
+ if (!["schema", "activation", "required", "fallback"].every((key) => Object.hasOwn(record, key)) || typeof record["required"] !== "boolean")
17743
+ throw invalid();
17744
+ try {
17745
+ normalizeProfileConfigBinding(record);
17746
+ } catch {
17747
+ throw invalid();
17748
+ }
17749
+ }
17750
+ }
17751
+ function readManagedManifest(targetHome) {
17752
+ const path = join19(targetHome, SESSION_RENDER_MANIFEST_RELATIVE_PATH);
17753
+ let ancestor = dirname11(path);
17754
+ while (true) {
17755
+ const stat = lstatSync8(ancestor);
17756
+ if (stat.isSymbolicLink() || !stat.isDirectory())
17757
+ throw new Error("SESSION_REFRESH_PATH_INVALID: managed target uses a symlink or non-directory ancestor.");
17758
+ const parent = dirname11(ancestor);
17759
+ if (parent === ancestor)
17760
+ break;
17761
+ ancestor = parent;
17762
+ }
17763
+ const fd = openSync4(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
17764
+ let raw;
17765
+ try {
17766
+ const stat = fstatSync4(fd);
17767
+ if (!stat.isFile() || stat.size > SESSION_MANAGED_INPUT_MAX_BYTES)
17768
+ throw new Error("SESSION_REFRESH_MANIFEST_INVALID: expected a bounded regular manifest file.");
17769
+ raw = readFileSync14(fd);
17770
+ if (raw.byteLength > SESSION_MANAGED_INPUT_MAX_BYTES)
17771
+ throw new Error("SESSION_REFRESH_MANIFEST_INVALID: manifest grew beyond the read limit.");
17772
+ } finally {
17773
+ closeSync4(fd);
17774
+ }
17775
+ const manifest = JSON.parse(raw.toString("utf8"));
17776
+ if (manifest.schema !== SESSION_RENDER_SCHEMA || !SESSION_RENDER_TOOLS.includes(manifest.tool) || manifest.targetHome !== targetHome || !manifest.profile?.trim() || !["session-home", "project-root"].includes(manifest.targetKind) || !Array.isArray(manifest.files) || !manifest.refreshSelector || manifest.targetOwner?.writer?.id !== SESSION_RENDERER_OWNER_ID || manifest.targetOwner?.targetHome !== targetHome) {
17777
+ throw new Error("SESSION_REFRESH_MANIFEST_INVALID: target is not an owned hosted profile render; apply a hosted --compile-profile first.");
17778
+ }
17779
+ const recordedProjectRoot = manifest.targetOwner.projectRoot;
17780
+ if (recordedProjectRoot !== null && recordedProjectRoot !== undefined) {
17781
+ if (typeof recordedProjectRoot !== "string" || !isAbsolute9(recordedProjectRoot)) {
17782
+ throw new Error("SESSION_REFRESH_MANIFEST_INVALID: recorded project root must be an absolute path.");
17783
+ }
17784
+ const normalizedProjectRoot = resolve13(recordedProjectRoot);
17785
+ if (normalizedProjectRoot === parse5(normalizedProjectRoot).root || manifest.targetKind === "project-root" && normalizedProjectRoot !== targetHome || manifest.targetKind === "session-home" && manifest.tool !== "opencode") {
17786
+ throw new Error("SESSION_REFRESH_MANIFEST_INVALID: recorded project root does not match the managed target.");
17787
+ }
17788
+ manifest.targetOwner.projectRoot = normalizedProjectRoot;
17789
+ } else if (manifest.targetKind === "project-root") {
17790
+ throw new Error("SESSION_REFRESH_MANIFEST_INVALID: project-scoped render is missing its recorded project root.");
17791
+ }
17792
+ manifest.refreshSelector = normalizeSessionHostedProfileSelector(manifest.refreshSelector);
17793
+ return { manifest, sha256: createHash10("sha256").update(raw).digest("hex") };
17794
+ }
17795
+ async function refreshSessionRender(input) {
17796
+ const { store } = input;
17797
+ if (store.mode !== "api" || !store.v1BaseUrl)
17798
+ throw new Error("SESSION_REFRESH_HOSTED_REQUIRED: refresh requires the configured hosted Instructions API.");
17799
+ const targetHome = resolve13(input.targetHome);
17800
+ if (targetHome === parse5(targetHome).root)
17801
+ throw new Error("SESSION_REFRESH_PATH_INVALID: filesystem root is not a managed target.");
17802
+ const previous = readManagedManifest(targetHome);
17803
+ const selector = previous.manifest.refreshSelector;
17804
+ if (authority(store.v1BaseUrl) !== selector.authority)
17805
+ throw new Error("SESSION_REFRESH_AUTHORITY_MISMATCH: configured API differs from the recorded profile authority.");
17806
+ const profile = await store.getProfile(selector.profileId);
17807
+ if (profile.id !== selector.profileId)
17808
+ throw new Error("SESSION_REFRESH_PROFILE_MISMATCH: hosted profile identity changed.");
17809
+ const [configs, bindings, assetBindings] = await Promise.all([
17810
+ store.getProfileConfigs(profile.id),
17811
+ store.getProfileConfigBindings(profile.id, { requireExplicit: true }),
17812
+ store.getProfileAssetBindings(profile.id, { requireExplicit: true })
17813
+ ]);
17814
+ assertHostedProfileBindings(profile.id, configs, bindings);
17815
+ if (configs.length > 2048 || bindings.length > 8192 || assetBindings.length > 1024)
17816
+ throw new Error("SESSION_REFRESH_INPUT_LIMIT: hosted profile exceeds refresh bounds.");
17817
+ const totalBytes = configs.reduce((sum, config) => sum + Buffer.byteLength(config.content), 0);
17818
+ if (totalBytes > SESSION_MANAGED_INPUT_MAX_BYTES)
17819
+ throw new Error("SESSION_REFRESH_INPUT_LIMIT: hosted instruction payload exceeds refresh byte bounds.");
17820
+ const assetConfigs = await Promise.all([...new Set(assetBindings.map((binding) => binding.source_config_id))].map((id) => store.getConfigById(id)));
17821
+ if (assetConfigs.reduce((sum, config) => sum + Buffer.byteLength(config.content), totalBytes) > SESSION_MANAGED_INPUT_MAX_BYTES)
17822
+ throw new Error("SESSION_REFRESH_INPUT_LIMIT: hosted source and asset payload exceeds refresh byte bounds.");
17823
+ let ownedClaudeAuthorities;
17824
+ if (previous.manifest.tool === "claude") {
17825
+ ownedClaudeAuthorities = (await store.listConfigs({ category: "rules", agent: "claude", kind: "file" })).filter((config) => config.target_path && basename6(normalizeTargetPath(config.target_path)) === "AGENTS.md").map((config) => ({ slug: config.slug, targetPath: config.target_path, content: config.content }));
17826
+ }
17827
+ const station = selector.stationProfile ? stationProfileSource() : null;
17828
+ const plan = planProfileSessionRender({
17829
+ tool: previous.manifest.tool,
17830
+ profile: previous.manifest.profile,
17831
+ profile_id: profile.id,
17832
+ provider_version: selector.providerVersion,
17833
+ targetHome,
17834
+ ...previous.manifest.targetKind === "project-root" ? { projectRoot: targetHome } : previous.manifest.targetOwner.projectRoot ? { projectRoot: previous.manifest.targetOwner.projectRoot } : {},
17835
+ sessionId: previous.manifest.sessionId ?? undefined,
17836
+ refreshSelector: selector,
17837
+ codewithNativeImports: selector.codewithNativeImports,
17838
+ allowEmptySources: selector.allowEmptySources,
17839
+ configs,
17840
+ bindings,
17841
+ asset_configs: assetConfigs,
17842
+ asset_bindings: assetBindings,
17843
+ asset_plan_mode: "apply",
17844
+ asset_scope: selector.assetScope,
17845
+ asset_surface: selector.assetSurface,
17846
+ extra_sources: station ? [station] : undefined,
17847
+ ownedClaudeAuthorities,
17848
+ graph_context: {
17849
+ ...selector.providerVariant ? { provider_variant: selector.providerVariant } : {},
17850
+ ...selector.model ? { model: selector.model } : {},
17851
+ ...selector.path ? { path: selector.path } : {},
17852
+ ...selector.manual.length ? { manual: selector.manual } : {}
17853
+ }
17854
+ });
17855
+ if (selector.checkGlobalCoverage) {
17856
+ const coverage = computeGlobalSourceCoverage(await store.listConfigs({}), accountedGlobalSourceSlugs(plan.manifest));
17857
+ if (!coverage.complete)
17858
+ throw new Error(`SESSION_REFRESH_COVERAGE_INCOMPLETE: ${coverage.missingSlugs.length} registered global sources are absent.`);
17859
+ }
17860
+ const preview = applySessionRender(plan, { dryRun: true, expectedManifestSha256: previous.sha256, ownedClaudeAuthorities });
17861
+ const common = { checkedAt: new Date().toISOString(), targetHome, selector, sourceHash: plan.manifest.sourceHash };
17862
+ if (preview.conflicts.length)
17863
+ return { ...common, status: "blocked", apply: preview };
17864
+ if (!preview.drift.clean)
17865
+ throw new Error("SESSION_REFRESH_DRIFT: generated files changed or disappeared since their last managed render.");
17866
+ const sameSources = previous.manifest.sourceHash === plan.manifest.sourceHash;
17867
+ const samePayloads = preview.files.filter((file) => file.role !== "manifest").every((file) => file.action === "unchanged");
17868
+ if (sameSources && samePayloads) {
17869
+ if (readManagedManifest(targetHome).sha256 !== previous.sha256 || !checkSessionRenderDrift(targetHome).clean)
17870
+ throw new Error("SESSION_REFRESH_DRIFT: managed target changed during refresh.");
17871
+ return { ...common, status: "unchanged", apply: { ...preview, files: preview.files.map((file) => file.role === "manifest" ? { ...file, action: "unchanged", changed: false, newSha256: previous.sha256, reason: "hosted sources and generated payloads unchanged" } : file) } };
17872
+ }
17873
+ if (input.dryRun)
17874
+ return { ...common, status: "dry-run", apply: preview };
17875
+ const applied = applySessionRender(plan, { expectedManifestSha256: previous.sha256, ownedClaudeAuthorities });
17876
+ if (applied.conflicts.length)
17877
+ return { ...common, status: "blocked", apply: applied };
17878
+ if (!applied.applied || !checkSessionRenderDrift(targetHome).clean)
17879
+ throw new Error("SESSION_REFRESH_READBACK_FAILED: applied instructions did not verify against their manifest.");
17880
+ return { ...common, status: "updated", apply: applied };
17243
17881
  }
17244
17882
 
17245
17883
  // src/index.ts
@@ -17248,9 +17886,11 @@ init_transforms();
17248
17886
  init_machine();
17249
17887
 
17250
17888
  // src/lib/platform-profiles.ts
17889
+ init_types();
17251
17890
  init_config_store();
17252
17891
 
17253
17892
  // src/lib/project-dashboard-standard.ts
17893
+ init_types();
17254
17894
  init_config_store();
17255
17895
  var PROJECT_DASHBOARD_STANDARD_SLUG = "agent-managed-project-dashboard-standard";
17256
17896
  var PROJECT_DASHBOARD_PROFILE_VARIABLES = {
@@ -17343,11 +17983,15 @@ async function ensureProjectDashboardStandardConfig(store = resolveConfigStore()
17343
17983
  };
17344
17984
  try {
17345
17985
  const existing = await store.getConfig(PROJECT_DASHBOARD_STANDARD_SLUG);
17986
+ if (isRetiredInstructionSource(existing))
17987
+ return existing;
17346
17988
  if (existing.content !== input.content || existing.description !== input.description || existing.category !== input.category || existing.agent !== input.agent || existing.format !== input.format || existing.kind !== input.kind) {
17347
17989
  return await store.updateConfig(existing.id, input);
17348
17990
  }
17349
17991
  return existing;
17350
- } catch {
17992
+ } catch (error) {
17993
+ if (!(error instanceof ConfigNotFoundError))
17994
+ throw error;
17351
17995
  return await store.createConfig(input);
17352
17996
  }
17353
17997
  }
@@ -17360,10 +18004,9 @@ function profileHasSelectors2(profile) {
17360
18004
  var PLATFORM_PROFILE_PRESETS = [
17361
18005
  {
17362
18006
  name: "linux-arm64",
17363
- description: "Default Linux arm64 profile for linux-node-a/linux-node-b-style machines",
17364
- selectors: { os: ["linux"], arch: ["arm64"], hostnames: ["linux-node-a", "linux-node-b", "station01"] },
18007
+ description: "Default reviewed instruction profile for Linux arm64",
18008
+ selectors: { os: ["linux"], arch: ["arm64"] },
17365
18009
  variables: {
17366
- WORKSPACE_ROOT: "{{HOME_DIR}}/workspace",
17367
18010
  BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
17368
18011
  BUN_PATH: "{{BUN_BIN_DIR}}/bun",
17369
18012
  PATH_PREFIX: "{{BUN_BIN_DIR}}",
@@ -17372,10 +18015,9 @@ var PLATFORM_PROFILE_PRESETS = [
17372
18015
  },
17373
18016
  {
17374
18017
  name: "macos-arm64",
17375
- description: "Default macOS arm64 profile for macos-node-a/macos-node-b-style machines",
17376
- selectors: { os: ["macos"], arch: ["arm64"], hostnames: ["macos-node-a", "macos-node-b"] },
18018
+ description: "Default reviewed instruction profile for macOS arm64",
18019
+ selectors: { os: ["macos"], arch: ["arm64"] },
17377
18020
  variables: {
17378
- WORKSPACE_ROOT: "{{HOME_DIR}}/Workspace",
17379
18021
  BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
17380
18022
  BUN_PATH: "/opt/homebrew/bin/bun",
17381
18023
  PATH_PREFIX: "/opt/homebrew/bin:{{BUN_BIN_DIR}}",
@@ -17384,12 +18026,21 @@ var PLATFORM_PROFILE_PRESETS = [
17384
18026
  }
17385
18027
  ];
17386
18028
  async function ensurePlatformProfiles(store = resolveConfigStore()) {
17387
- const configs = await store.listConfigs();
17388
18029
  const ensured = [];
17389
- for (const preset of PLATFORM_PROFILE_PRESETS) {
17390
- let profile;
18030
+ const existingProfiles = await Promise.all(PLATFORM_PROFILE_PRESETS.map(async (preset) => {
17391
18031
  try {
17392
- profile = await store.getProfile(preset.name);
18032
+ return await store.getProfile(preset.name);
18033
+ } catch (error) {
18034
+ if (error instanceof ProfileNotFoundError)
18035
+ return null;
18036
+ throw error;
18037
+ }
18038
+ }));
18039
+ for (const [index, preset] of PLATFORM_PROFILE_PRESETS.entries()) {
18040
+ let profile;
18041
+ const existing = existingProfiles[index];
18042
+ if (existing) {
18043
+ profile = existing;
17393
18044
  const selectors = mergeProfileSelectors(preset.selectors, profile.selectors);
17394
18045
  const variables = mergeProfileVariables(preset.variables, profile.variables);
17395
18046
  if (JSON.stringify(selectors) !== JSON.stringify(profile.selectors) || JSON.stringify(variables) !== JSON.stringify(profile.variables)) {
@@ -17399,12 +18050,9 @@ async function ensurePlatformProfiles(store = resolveConfigStore()) {
17399
18050
  variables
17400
18051
  });
17401
18052
  }
17402
- } catch {
18053
+ } else {
17403
18054
  profile = await store.createProfile(preset);
17404
18055
  }
17405
- for (const config of configs) {
17406
- await store.addConfigToProfile(profile.id, config.id);
17407
- }
17408
18056
  ensured.push(profile);
17409
18057
  }
17410
18058
  return ensured;
@@ -17438,6 +18086,7 @@ function mergeUnique(preset, existing) {
17438
18086
  init_global_agent_rules_standard();
17439
18087
 
17440
18088
  // src/lib/dangerous-operation-guard-standard.ts
18089
+ init_types();
17441
18090
  init_config_store();
17442
18091
  var DANGEROUS_OPERATION_GUARD_STANDARD_SLUG = "dangerous-operation-guard-standard";
17443
18092
  var DANGEROUS_OPERATION_GUARD_STANDARD_CONTENT = `# Dangerous Operation Guard Standard
@@ -17549,11 +18198,15 @@ async function ensureDangerousOperationGuardStandardConfig(store = resolveConfig
17549
18198
  };
17550
18199
  try {
17551
18200
  const existing = await store.getConfig(DANGEROUS_OPERATION_GUARD_STANDARD_SLUG);
18201
+ if (isRetiredInstructionSource(existing))
18202
+ return existing;
17552
18203
  if (existing.content !== input.content || existing.description !== input.description || existing.category !== input.category || existing.agent !== input.agent || existing.format !== input.format || existing.kind !== input.kind || JSON.stringify(existing.tags) !== JSON.stringify(input.tags)) {
17553
18204
  return await store.updateConfig(existing.id, input);
17554
18205
  }
17555
18206
  return existing;
17556
- } catch {
18207
+ } catch (error) {
18208
+ if (!(error instanceof ConfigNotFoundError))
18209
+ throw error;
17557
18210
  return await store.createConfig(input);
17558
18211
  }
17559
18212
  }
@@ -17563,8 +18216,8 @@ init_codewith_shared_todos_storage_standard();
17563
18216
 
17564
18217
  // src/lib/sync.ts
17565
18218
  init_config_store();
17566
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync16 } from "fs";
17567
- import { basename as basename6, extname as extname3, join as join19 } from "path";
18219
+ import { existsSync as existsSync16, readdirSync as readdirSync5, readFileSync as readFileSync16 } from "fs";
18220
+ import { basename as basename7, extname as extname3, join as join21 } from "path";
17568
18221
  init_config_agents();
17569
18222
  init_redact();
17570
18223
  init_machine();
@@ -17572,8 +18225,8 @@ init_transforms();
17572
18225
 
17573
18226
  // src/lib/sync-dir.ts
17574
18227
  init_config_store();
17575
- import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync15, statSync as statSync6 } from "fs";
17576
- import { join as join18, relative as relative7 } from "path";
18228
+ import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync15, statSync as statSync7 } from "fs";
18229
+ import { join as join20, relative as relative6 } from "path";
17577
18230
  init_redact();
17578
18231
  var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
17579
18232
  function shouldSkip(p) {
@@ -17582,9 +18235,9 @@ function shouldSkip(p) {
17582
18235
  async function syncFromDir(dir, opts = {}) {
17583
18236
  const store = opts.store ?? resolveConfigStore();
17584
18237
  const absDir = expandPath(dir);
17585
- if (!existsSync16(absDir))
18238
+ if (!existsSync15(absDir))
17586
18239
  return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
17587
- const files = opts.recursive !== false ? walkDir(absDir) : readdirSync4(absDir).map((f) => join18(absDir, f)).filter((f) => statSync6(f).isFile());
18240
+ const files = opts.recursive !== false ? walkDir(absDir) : readdirSync4(absDir).map((f) => join20(absDir, f)).filter((f) => statSync7(f).isFile());
17588
18241
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
17589
18242
  const allConfigs = await store.listConfigs();
17590
18243
  for (const file of files) {
@@ -17603,7 +18256,7 @@ async function syncFromDir(dir, opts = {}) {
17603
18256
  const existing = allConfigs.find((c) => c.target_path === targetPath);
17604
18257
  if (!existing) {
17605
18258
  if (!opts.dryRun)
17606
- await store.createConfig({ name: relative7(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content: redacted.content });
18259
+ await store.createConfig({ name: relative6(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content: redacted.content });
17607
18260
  result.added++;
17608
18261
  } else if (existing.content !== redacted.content) {
17609
18262
  if (!opts.dryRun)
@@ -17643,7 +18296,7 @@ async function syncToDir(dir, opts = {}) {
17643
18296
  }
17644
18297
  function walkDir(dir, files = []) {
17645
18298
  for (const entry of readdirSync4(dir, { withFileTypes: true })) {
17646
- const full = join18(dir, entry.name);
18299
+ const full = join20(dir, entry.name);
17647
18300
  if (shouldSkip(full))
17648
18301
  continue;
17649
18302
  if (entry.isDirectory())
@@ -17665,7 +18318,7 @@ var CLAUDE_PROMPT_OUTPUTS = [
17665
18318
  { agent: "qwen", target_path: "~/.qwen/QWEN.md", transform: "codex-flat" }
17666
18319
  ];
17667
18320
  function claudeRuleOutputs(fileName) {
17668
- const stem = basename6(fileName, extname3(fileName));
18321
+ const stem = basename7(fileName, extname3(fileName));
17669
18322
  return [
17670
18323
  { agent: "cursor", target_path: `~/.cursor/rules/${stem}.mdc`, transform: "cursor-mdc" }
17671
18324
  ];
@@ -17704,15 +18357,15 @@ function isGeneratedOutputTarget2(config, owners) {
17704
18357
  return !!ownerIds && !ownerIds.has(config.id);
17705
18358
  }
17706
18359
  function hasClaudePromptSource() {
17707
- return existsSync17(expandPath("~/.claude/CLAUDE.md"));
18360
+ return existsSync16(expandPath("~/.claude/CLAUDE.md"));
17708
18361
  }
17709
18362
  function hasClaudeRuleSourceForCursorTarget(targetPath) {
17710
18363
  const absoluteTargetPath = expandPath(targetPath);
17711
18364
  const absolutePrefix = expandPath("~/.cursor/rules");
17712
18365
  if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
17713
18366
  return false;
17714
- const stem = basename6(absoluteTargetPath, ".mdc");
17715
- return existsSync17(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync17(expandPath(`~/.claude/rules/${stem}.mdc`));
18367
+ const stem = basename7(absoluteTargetPath, ".mdc");
18368
+ return existsSync16(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync16(expandPath(`~/.claude/rules/${stem}.mdc`));
17716
18369
  }
17717
18370
  function isKnownGeneratedTargetPath(targetPath) {
17718
18371
  const normalizedTargetPath = normalizeTargetPath(targetPath);
@@ -17777,8 +18430,8 @@ async function syncProject(opts) {
17777
18430
  const allConfigs = await store.listConfigs();
17778
18431
  const machine = detectMachineContext();
17779
18432
  for (const pf of PROJECT_CONFIG_FILES) {
17780
- const abs = join19(absDir, pf.file);
17781
- if (!existsSync17(abs))
18433
+ const abs = join21(absDir, pf.file);
18434
+ if (!existsSync16(abs))
17782
18435
  continue;
17783
18436
  try {
17784
18437
  const rawContent = readFileSync16(abs, "utf-8");
@@ -17810,19 +18463,19 @@ async function syncProject(opts) {
17810
18463
  }
17811
18464
  }
17812
18465
  for (const ruleDir of [
17813
- { dir: join19(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
17814
- { dir: join19(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
17815
- { dir: join19(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
17816
- { dir: join19(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
17817
- { dir: join19(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
17818
- { dir: join19(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
17819
- { dir: join19(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
18466
+ { dir: join21(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
18467
+ { dir: join21(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
18468
+ { dir: join21(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
18469
+ { dir: join21(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
18470
+ { dir: join21(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
18471
+ { dir: join21(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
18472
+ { dir: join21(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
17820
18473
  ]) {
17821
- if (!existsSync17(ruleDir.dir))
18474
+ if (!existsSync16(ruleDir.dir))
17822
18475
  continue;
17823
18476
  const mdFiles = readdirSync5(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
17824
18477
  for (const f of mdFiles) {
17825
- const abs = join19(ruleDir.dir, f);
18478
+ const abs = join21(ruleDir.dir, f);
17826
18479
  const raw = readFileSync16(abs, "utf-8");
17827
18480
  const redacted = redactContent(raw, "markdown");
17828
18481
  const machineAware = templateizeMachineContent(redacted.content, machine);
@@ -17862,14 +18515,14 @@ async function syncKnown(opts = {}) {
17862
18515
  for (const known of targets) {
17863
18516
  if (known.rulesDir) {
17864
18517
  const absDir = expandPath(known.rulesDir);
17865
- if (!existsSync17(absDir)) {
18518
+ if (!existsSync16(absDir)) {
17866
18519
  result.skipped.push(known.rulesDir);
17867
18520
  continue;
17868
18521
  }
17869
18522
  const extensions = known.rulesExtensions ?? [".md", ".mdc"];
17870
18523
  const ruleFiles = readdirSync5(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
17871
18524
  for (const f of ruleFiles) {
17872
- const abs2 = join19(absDir, f);
18525
+ const abs2 = join21(absDir, f);
17873
18526
  const targetPath = abs2.replace(home, "~");
17874
18527
  if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
17875
18528
  result.skipped.push(`${targetPath} (generated output)`);
@@ -17903,7 +18556,7 @@ async function syncKnown(opts = {}) {
17903
18556
  continue;
17904
18557
  }
17905
18558
  const abs = expandPath(known.path);
17906
- if (!existsSync17(abs)) {
18559
+ if (!existsSync16(abs)) {
17907
18560
  result.skipped.push(known.path);
17908
18561
  continue;
17909
18562
  }
@@ -18011,7 +18664,7 @@ function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
18011
18664
  }
18012
18665
  function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
18013
18666
  const path = expandPath(targetPath);
18014
- if (!existsSync17(path))
18667
+ if (!existsSync16(path))
18015
18668
  return `(file not found on disk: ${path})`;
18016
18669
  const diskContent = readFileSync16(path, "utf-8");
18017
18670
  if (diskContent === expectedContent)
@@ -18174,8 +18827,8 @@ function detectFormat(filePath) {
18174
18827
  init_types();
18175
18828
  init_config_store();
18176
18829
  import { createHash as createHash11 } from "crypto";
18177
- import { existsSync as existsSync18, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
18178
- import { dirname as dirname11, join as join20, resolve as resolve12 } from "path";
18830
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
18831
+ import { dirname as dirname12, join as join22, resolve as resolve14 } from "path";
18179
18832
  import { tmpdir } from "os";
18180
18833
  var ARCHIVE_EXCLUSIONS = [
18181
18834
  {
@@ -18197,7 +18850,7 @@ var ARCHIVE_EXCLUSIONS = [
18197
18850
  function compareText(left, right) {
18198
18851
  return left < right ? -1 : left > right ? 1 : 0;
18199
18852
  }
18200
- function sha25611(value) {
18853
+ function sha25610(value) {
18201
18854
  return createHash11("sha256").update(value).digest("hex");
18202
18855
  }
18203
18856
  function canonicalize(value) {
@@ -18322,19 +18975,19 @@ function computeIntegrity(domain, options) {
18322
18975
  machines: collections.machines.length
18323
18976
  };
18324
18977
  const hashes = {
18325
- configs: sha25611(canonicalDomainJson(collections.configs)),
18326
- config_snapshots: sha25611(canonicalDomainJson(collections.config_snapshots)),
18327
- profiles: sha25611(canonicalDomainJson(collections.profiles)),
18328
- profile_config_bindings: sha25611(canonicalDomainJson(collections.profile_config_bindings)),
18329
- profile_asset_bindings: sha25611(canonicalDomainJson(collections.profile_asset_bindings)),
18330
- machines: sha25611(canonicalDomainJson(collections.machines))
18978
+ configs: sha25610(canonicalDomainJson(collections.configs)),
18979
+ config_snapshots: sha25610(canonicalDomainJson(collections.config_snapshots)),
18980
+ profiles: sha25610(canonicalDomainJson(collections.profiles)),
18981
+ profile_config_bindings: sha25610(canonicalDomainJson(collections.profile_config_bindings)),
18982
+ profile_asset_bindings: sha25610(canonicalDomainJson(collections.profile_asset_bindings)),
18983
+ machines: sha25610(canonicalDomainJson(collections.machines))
18331
18984
  };
18332
18985
  return {
18333
18986
  algorithm: "sha256",
18334
18987
  canonicalization: options.canonicalization,
18335
18988
  counts,
18336
18989
  hashes,
18337
- domain_sha256: sha25611(canonicalDomainJson({ counts, hashes }))
18990
+ domain_sha256: sha25610(canonicalDomainJson({ counts, hashes }))
18338
18991
  };
18339
18992
  }
18340
18993
  function computeDomainIntegrity(domain) {
@@ -18505,19 +19158,19 @@ async function exportConfigs(outputPath, opts = {}) {
18505
19158
  exported_at: new Date().toISOString(),
18506
19159
  payload: {
18507
19160
  path: "domain.json",
18508
- sha256: sha25611(payload),
19161
+ sha256: sha25610(payload),
18509
19162
  size_bytes: Buffer.byteLength(payload)
18510
19163
  },
18511
19164
  integrity,
18512
19165
  exclusions: ARCHIVE_EXCLUSIONS
18513
19166
  };
18514
- const absOutput = resolve12(outputPath);
18515
- mkdirSync8(dirname11(absOutput), { recursive: true });
18516
- const stagingDir = mkdtempSync(join20(tmpdir(), "instructions-domain-export-"));
19167
+ const absOutput = resolve14(outputPath);
19168
+ mkdirSync7(dirname12(absOutput), { recursive: true });
19169
+ const stagingDir = mkdtempSync(join22(tmpdir(), "instructions-domain-export-"));
18517
19170
  try {
18518
- writeFileSync6(join20(stagingDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
19171
+ writeFileSync5(join22(stagingDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
18519
19172
  `, "utf-8");
18520
- writeFileSync6(join20(stagingDir, "domain.json"), payload, "utf-8");
19173
+ writeFileSync5(join22(stagingDir, "domain.json"), payload, "utf-8");
18521
19174
  const proc = Bun.spawn(["tar", "czf", absOutput, "-C", stagingDir, "manifest.json", "domain.json"], {
18522
19175
  stdout: "pipe",
18523
19176
  stderr: "pipe"
@@ -18529,15 +19182,15 @@ async function exportConfigs(outputPath, opts = {}) {
18529
19182
  }
18530
19183
  return { path: absOutput, count: domain.configs.length, counts: integrity.counts, integrity };
18531
19184
  } finally {
18532
- if (existsSync18(stagingDir))
18533
- rmSync4(stagingDir, { recursive: true, force: true });
19185
+ if (existsSync17(stagingDir))
19186
+ rmSync3(stagingDir, { recursive: true, force: true });
18534
19187
  }
18535
19188
  }
18536
19189
  // src/lib/import.ts
18537
19190
  init_types();
18538
19191
  init_config_store();
18539
19192
  import { createHash as createHash12 } from "crypto";
18540
- import { resolve as resolve13 } from "path";
19193
+ import { resolve as resolve15 } from "path";
18541
19194
  function compareText2(left, right) {
18542
19195
  return left < right ? -1 : left > right ? 1 : 0;
18543
19196
  }
@@ -18561,7 +19214,7 @@ function emptyResult() {
18561
19214
  integrity: null
18562
19215
  };
18563
19216
  }
18564
- function sha25612(value) {
19217
+ function sha25611(value) {
18565
19218
  return createHash12("sha256").update(value).digest("hex");
18566
19219
  }
18567
19220
  function normalizeMemberName(name) {
@@ -18632,7 +19285,7 @@ async function readV2(reader, manifest) {
18632
19285
  throw new Error("Invalid v2 archive manifest");
18633
19286
  }
18634
19287
  const payload = await reader.read("domain.json");
18635
- if (Buffer.byteLength(payload) !== manifest.payload.size_bytes || sha25612(payload) !== manifest.payload.sha256) {
19288
+ if (Buffer.byteLength(payload) !== manifest.payload.size_bytes || sha25611(payload) !== manifest.payload.sha256) {
18636
19289
  throw new Error("Archive integrity failure: domain payload hash or size does not match the manifest");
18637
19290
  }
18638
19291
  const domain = validateInstructionsDomainArchive(JSON.parse(payload));
@@ -18825,7 +19478,7 @@ async function importV1(reader, manifest, store, conflict) {
18825
19478
  async function importConfigs(bundlePath, opts = {}) {
18826
19479
  const store = opts.store ?? resolveConfigStore();
18827
19480
  const conflict = opts.conflict ?? "skip";
18828
- const reader = await openArchive(resolve13(bundlePath));
19481
+ const reader = await openArchive(resolve15(bundlePath));
18829
19482
  const manifestText = await reader.read("manifest.json");
18830
19483
  let manifest;
18831
19484
  try {
@@ -18853,9 +19506,9 @@ init_redact();
18853
19506
 
18854
19507
  // src/lib/package-manager-guard.ts
18855
19508
  import { execFileSync as execFileSync2 } from "child_process";
18856
- import { existsSync as existsSync19, lstatSync as lstatSync7, readdirSync as readdirSync6, readFileSync as readFileSync17 } from "fs";
18857
- import { homedir as homedir10 } from "os";
18858
- import { basename as basename7, dirname as dirname12, isAbsolute as isAbsolute8, join as join21, relative as relative8, resolve as resolve14 } from "path";
19509
+ import { existsSync as existsSync18, lstatSync as lstatSync9, readdirSync as readdirSync6, readFileSync as readFileSync17 } from "fs";
19510
+ import { homedir as homedir11 } from "os";
19511
+ import { basename as basename8, dirname as dirname13, isAbsolute as isAbsolute10, join as join23, relative as relative7, resolve as resolve16 } from "path";
18859
19512
  var SKIP_DIRS = new Set([
18860
19513
  ".git",
18861
19514
  "node_modules",
@@ -18892,14 +19545,14 @@ var TOKEN_VALUE_PATTERNS = [
18892
19545
  { re: /xoxb-[0-9]+-[A-Za-z0-9-]+/, rule: "literal-slack-token", detail: "literal Slack token-like value" }
18893
19546
  ];
18894
19547
  function scanPackageManagerSecrets(options = {}) {
18895
- const cwd = options.cwd ? resolve14(options.cwd) : process.cwd();
18896
- const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve14(cwd, root));
19548
+ const cwd = options.cwd ? resolve16(options.cwd) : process.cwd();
19549
+ const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve16(cwd, root));
18897
19550
  const findings = [];
18898
19551
  let scannedFiles = 0;
18899
19552
  for (const root of roots) {
18900
- if (!existsSync19(root))
19553
+ if (!existsSync18(root))
18901
19554
  continue;
18902
- const stat = lstatSync7(root);
19555
+ const stat = lstatSync9(root);
18903
19556
  if (stat.isFile()) {
18904
19557
  if (!shouldScanRepoFile(root))
18905
19558
  continue;
@@ -18907,14 +19560,14 @@ function scanPackageManagerSecrets(options = {}) {
18907
19560
  if (text === null)
18908
19561
  continue;
18909
19562
  scannedFiles++;
18910
- findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname12(root)));
19563
+ findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname13(root)));
18911
19564
  continue;
18912
19565
  }
18913
19566
  if (!stat.isDirectory())
18914
19567
  continue;
18915
19568
  const tracked = trackedFiles(root);
18916
19569
  for (const file of collectRepoFiles(root)) {
18917
- const rel = toPosix(relative8(root, file));
19570
+ const rel = toPosix(relative7(root, file));
18918
19571
  const isTracked = tracked.has(rel);
18919
19572
  const text = readTextFile(file);
18920
19573
  if (text === null)
@@ -18924,10 +19577,10 @@ function scanPackageManagerSecrets(options = {}) {
18924
19577
  }
18925
19578
  }
18926
19579
  if (options.includeHome) {
18927
- const home = homedir10();
19580
+ const home = homedir11();
18928
19581
  for (const name of HOME_FILES) {
18929
- const file = join21(home, name);
18930
- if (!existsSync19(file))
19582
+ const file = join23(home, name);
19583
+ if (!existsSync18(file))
18931
19584
  continue;
18932
19585
  const text = readTextFile(file);
18933
19586
  if (text === null)
@@ -18951,12 +19604,12 @@ function collectRepoFiles(root) {
18951
19604
  if (entry.isDirectory()) {
18952
19605
  if (SKIP_DIRS.has(entry.name))
18953
19606
  continue;
18954
- visit(join21(dir, entry.name));
19607
+ visit(join23(dir, entry.name));
18955
19608
  continue;
18956
19609
  }
18957
19610
  if (!entry.isFile())
18958
19611
  continue;
18959
- const file = join21(dir, entry.name);
19612
+ const file = join23(dir, entry.name);
18960
19613
  if (shouldScanRepoFile(file))
18961
19614
  out.push(file);
18962
19615
  }
@@ -18965,11 +19618,11 @@ function collectRepoFiles(root) {
18965
19618
  return out;
18966
19619
  }
18967
19620
  function shouldScanRepoFile(file) {
18968
- const name = basename7(file);
19621
+ const name = basename8(file);
18969
19622
  return isNpmrcName(name) || isBunConfigName(name) || LOCKFILE_NAMES.has(name);
18970
19623
  }
18971
19624
  function classifyRepoFile(file) {
18972
- const name = basename7(file);
19625
+ const name = basename8(file);
18973
19626
  if (isNpmrcName(name))
18974
19627
  return "repo-npmrc";
18975
19628
  if (isBunConfigName(name))
@@ -18991,7 +19644,7 @@ function isNpmrcName(name) {
18991
19644
  }
18992
19645
  function readTextFile(file) {
18993
19646
  try {
18994
- const stat = lstatSync7(file);
19647
+ const stat = lstatSync9(file);
18995
19648
  if (!stat.isFile() || stat.size > 5000000)
18996
19649
  return null;
18997
19650
  const buf = readFileSync17(file);
@@ -19194,11 +19847,11 @@ function trackedFiles(root) {
19194
19847
  }
19195
19848
  function isTrackedFile(file) {
19196
19849
  try {
19197
- const repoRoot = execFileSync2("git", ["-C", dirname12(file), "rev-parse", "--show-toplevel"], {
19850
+ const repoRoot = execFileSync2("git", ["-C", dirname13(file), "rev-parse", "--show-toplevel"], {
19198
19851
  encoding: "utf-8",
19199
19852
  stdio: ["ignore", "pipe", "ignore"]
19200
19853
  }).trim();
19201
- const rel = toPosix(relative8(repoRoot, file));
19854
+ const rel = toPosix(relative7(repoRoot, file));
19202
19855
  execFileSync2("git", ["-C", repoRoot, "ls-files", "--error-unmatch", "--", rel], {
19203
19856
  stdio: ["ignore", "ignore", "ignore"]
19204
19857
  });
@@ -19224,19 +19877,20 @@ function stripInlineComment(value) {
19224
19877
  return value.replace(/\s[#;].*$/, "").trim();
19225
19878
  }
19226
19879
  function displayPath(file, root) {
19227
- const home = homedir10();
19880
+ const home = homedir11();
19228
19881
  if (root === home && (file === home || file.startsWith(home + "/")))
19229
- return "~/" + toPosix(relative8(home, file));
19230
- if (isAbsolute8(root) && file.startsWith(root + "/"))
19231
- return toPosix(relative8(root, file));
19882
+ return "~/" + toPosix(relative7(home, file));
19883
+ if (isAbsolute10(root) && file.startsWith(root + "/"))
19884
+ return toPosix(relative7(root, file));
19232
19885
  if (file === home || file.startsWith(home + "/"))
19233
- return "~/" + toPosix(relative8(home, file));
19886
+ return "~/" + toPosix(relative7(home, file));
19234
19887
  return file;
19235
19888
  }
19236
19889
  function toPosix(path) {
19237
19890
  return path.split("\\").join("/");
19238
19891
  }
19239
19892
  export {
19893
+ validateExpectedConfigVersion,
19240
19894
  uuid,
19241
19895
  transformSkillContent,
19242
19896
  templateizeMachineContent,
@@ -19269,9 +19923,11 @@ export {
19269
19923
  renderTemplatePreview,
19270
19924
  renderTemplate,
19271
19925
  renderProviderFragment,
19926
+ renderNativeAgentContent,
19272
19927
  renderMachineAwareContentPreview,
19273
19928
  renderMachineAwareContent,
19274
19929
  refreshStationProfile,
19930
+ refreshSessionRender,
19275
19931
  redactContent,
19276
19932
  reconcileManagedSkillRuntimes,
19277
19933
  readStationProfile,
@@ -19285,6 +19941,7 @@ export {
19285
19941
  parseProjectContextBundle,
19286
19942
  parseAgentOperatingRulesVersion,
19287
19943
  now,
19944
+ normalizeSessionHostedProfileSelector,
19288
19945
  normalizeProfileConfigBinding,
19289
19946
  normalizeProfileAssetBinding,
19290
19947
  normalizeOsFamily,
@@ -19315,6 +19972,7 @@ export {
19315
19972
  ensureGlobalAgentRulesStandardConfig,
19316
19973
  ensureDangerousOperationGuardStandardConfig,
19317
19974
  ensureCodewithSharedTodosStorageStandardConfig,
19975
+ discoverHarnesses,
19318
19976
  diffConfig,
19319
19977
  detectMachineContext,
19320
19978
  detectFormat,
@@ -19331,6 +19989,7 @@ export {
19331
19989
  compareAgentOperatingRulesVersions,
19332
19990
  cleanSessionPathInput,
19333
19991
  checkSessionRenderDrift,
19992
+ buildSumiAgentsMd,
19334
19993
  buildStationProfileBlock,
19335
19994
  buildOpenCodeAgentsMd,
19336
19995
  buildCursorMdc,
@@ -19390,6 +20049,7 @@ export {
19390
20049
  LEGACY_CONFIGS_EXECUTABLE,
19391
20050
  LEGACY_CONFIGS_COMPAT_VERSION,
19392
20051
  KNOWN_CONFIGS,
20052
+ InvalidExpectedVersionError,
19393
20053
  InstructionGraphValidationError,
19394
20054
  INSTRUCTION_GRAPH_PLAN_SCHEMA,
19395
20055
  INSTRUCTION_FALLBACKS,
@@ -19398,10 +20058,13 @@ export {
19398
20058
  INSTRUCTIONS_LOCAL_OPT_IN_ENV,
19399
20059
  INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA,
19400
20060
  INBOX_CONVERSATIONS_MINIMUM_VERSION,
20061
+ HARNESS_DISCOVERY_SCHEMA,
19401
20062
  GLOBAL_AGENT_RULES_STANDARD_SLUG,
19402
20063
  GLOBAL_AGENT_RULES_STANDARD_CONTENT,
20064
+ DISCOVERABLE_HARNESSES,
19403
20065
  DANGEROUS_OPERATION_GUARD_STANDARD_SLUG,
19404
20066
  DANGEROUS_OPERATION_GUARD_STANDARD_CONTENT,
20067
+ ConfigVersionConflictError,
19405
20068
  ConfigNotFoundError,
19406
20069
  ConfigApplyError,
19407
20070
  CloudConfigStore,