@hasna/instructions 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -7
- package/dist/chunks/{apply-c3sd4agz.js → apply-ps6jjt6k.js} +3 -3
- package/dist/chunks/{apply-pnn7a3y7.js → apply-wj7wh5km.js} +3 -3
- package/dist/chunks/{index-pdpg8a44.js → index-18j7x3xw.js} +2 -2
- package/dist/chunks/{index-3djthcm6.js → index-27az1qb1.js} +28 -1
- package/dist/chunks/{index-ypcef3a2.js → index-ape24zc0.js} +2 -2
- package/dist/chunks/{index-jg8ptcwh.js → index-ek15201v.js} +2 -2
- package/dist/chunks/{index-6jncnnw7.js → index-n6nh6f5r.js} +119 -17
- package/dist/chunks/{index-aj66f6xr.js → index-pq18fdbj.js} +29 -6
- package/dist/chunks/{index-n3fjspr0.js → index-preyvj8n.js} +28 -1
- package/dist/chunks/{index-r7nfef2g.js → index-y8410ve0.js} +3 -3
- package/dist/chunks/{local-0a9gja58.js → local-1c1qxzvx.js} +15 -4
- package/dist/chunks/{local-969ye538.js → local-hc67bynj.js} +15 -4
- package/dist/chunks/{sync-q5hvwq6e.js → sync-12m3vs6g.js} +4 -4
- package/dist/chunks/{sync-xtkbm5br.js → sync-azd9t4p8.js} +4 -4
- package/dist/chunks/{template-wgnzdn7h.js → template-e4qrxs8p.js} +1 -1
- package/dist/chunks/{template-fsfxpe4p.js → template-nhn124zd.js} +1 -1
- package/dist/cli/index.js +596 -133
- package/dist/data/config-store.d.ts +12 -4
- package/dist/data/config-store.d.ts.map +1 -1
- package/dist/db/configs.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +658 -98
- package/dist/lib/asset-plan.d.ts.map +1 -1
- package/dist/lib/codewith-shared-todos-storage-standard.d.ts.map +1 -1
- package/dist/lib/dangerous-operation-guard-standard.d.ts.map +1 -1
- package/dist/lib/global-agent-rules-standard.d.ts.map +1 -1
- package/dist/lib/global-source-coverage.d.ts +12 -7
- package/dist/lib/global-source-coverage.d.ts.map +1 -1
- package/dist/lib/instruction-graph.d.ts +1 -1
- package/dist/lib/instruction-graph.d.ts.map +1 -1
- package/dist/lib/instruction-source-policy.d.ts +6 -0
- package/dist/lib/instruction-source-policy.d.ts.map +1 -0
- package/dist/lib/platform-profiles.d.ts.map +1 -1
- package/dist/lib/project-dashboard-standard.d.ts.map +1 -1
- package/dist/lib/session-apply.d.ts +24 -0
- package/dist/lib/session-apply.d.ts.map +1 -1
- package/dist/lib/session-refresh.d.ts +30 -0
- package/dist/lib/session-refresh.d.ts.map +1 -0
- package/dist/lib/session-render.d.ts +34 -1
- package/dist/lib/session-render.d.ts.map +1 -1
- package/dist/lib/transforms.d.ts +1 -0
- package/dist/lib/transforms.d.ts.map +1 -1
- package/dist/mcp/index.js +13 -12
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/sdk/index.js +19 -0
- package/dist/sdk/v1-client.d.ts +7 -1
- package/dist/sdk/v1-client.d.ts.map +1 -1
- package/dist/sdk/v1.generated.d.ts +20 -0
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/index.js +122 -8
- package/dist/server/openapi.d.ts +123 -0
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/cloud-store.d.ts.map +1 -1
- package/dist/types/index.d.ts +16 -2
- package/dist/types/index.d.ts.map +1 -1
- 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
|
-
|
|
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":
|
|
@@ -8050,6 +8114,7 @@ var init_asset_plan = __esm(() => {
|
|
|
8050
8114
|
codex: "cli",
|
|
8051
8115
|
cursor: "ide",
|
|
8052
8116
|
opencode: "cli",
|
|
8117
|
+
sumi: "cli",
|
|
8053
8118
|
codewith: "cli",
|
|
8054
8119
|
qwen: "cli",
|
|
8055
8120
|
aicopilot: "cli",
|
|
@@ -8698,9 +8763,19 @@ function skippedSource(source, reason) {
|
|
|
8698
8763
|
};
|
|
8699
8764
|
}
|
|
8700
8765
|
function normalizeSources(sources, tool, allowEmptySources) {
|
|
8701
|
-
const
|
|
8766
|
+
const providerSkipped = [];
|
|
8767
|
+
const normalized = sources.flatMap((source, index) => {
|
|
8702
8768
|
if (!source.id.trim())
|
|
8703
8769
|
throw new Error("Session instruction source id is required.");
|
|
8770
|
+
if (source.targetProviders?.length && !providerTargetsTool(source.targetProviders, tool)) {
|
|
8771
|
+
providerSkipped.push({
|
|
8772
|
+
id: source.id,
|
|
8773
|
+
label: source.label ?? source.id,
|
|
8774
|
+
targetProviders: source.targetProviders,
|
|
8775
|
+
reason: "instruction source targets a different provider"
|
|
8776
|
+
});
|
|
8777
|
+
return [];
|
|
8778
|
+
}
|
|
8704
8779
|
const floored = applyAgentOperatingRulesFloor(source, source.content ?? "");
|
|
8705
8780
|
const content = filterProviderOnlyBlocks(floored.content, tool);
|
|
8706
8781
|
const normalized2 = {
|
|
@@ -8719,7 +8794,7 @@ function normalizeSources(sources, tool, allowEmptySources) {
|
|
|
8719
8794
|
if (!allowEmptySources && !normalized2.content.trim() && normalized2.resolvedRules.length === 0 && !hasPathReferences) {
|
|
8720
8795
|
throw new Error(`Session instruction source "${source.id}" is empty. Pass --allow-empty-sources only for explicit empty renders.`);
|
|
8721
8796
|
}
|
|
8722
|
-
return normalized2;
|
|
8797
|
+
return [normalized2];
|
|
8723
8798
|
});
|
|
8724
8799
|
const originalOrder = [...normalized].sort(compareSessionInstructionSources);
|
|
8725
8800
|
const deduplicated = deduplicateSemanticPolicySources(normalized);
|
|
@@ -8727,7 +8802,7 @@ function normalizeSources(sources, tool, allowEmptySources) {
|
|
|
8727
8802
|
validateTargetedReplacementSources(originalOrder, ordered, deduplicated.skipped, tool);
|
|
8728
8803
|
rejectDuplicateSourceSlugs(ordered);
|
|
8729
8804
|
rejectDuplicateRulePaths(ordered);
|
|
8730
|
-
return { sources: ordered, skipped: deduplicated.skipped };
|
|
8805
|
+
return { sources: ordered, skipped: [...providerSkipped, ...deduplicated.skipped] };
|
|
8731
8806
|
}
|
|
8732
8807
|
function compareSessionInstructionSources(a, b) {
|
|
8733
8808
|
return SESSION_LAYER_RANK[a.resolvedLayer] - SESSION_LAYER_RANK[b.resolvedLayer] || a.resolvedOrder - b.resolvedOrder || a.id.localeCompare(b.id);
|
|
@@ -9332,6 +9407,11 @@ function isNativeProfileSessionTarget(input) {
|
|
|
9332
9407
|
return (input.tool === "grok" || input.tool === "devin") && Boolean(input.targetHome?.trim()) && !input.projectRoot;
|
|
9333
9408
|
}
|
|
9334
9409
|
function adapterFor(input) {
|
|
9410
|
+
if (input.tool === "sumi" && input.projectRoot) {
|
|
9411
|
+
if (input.providerSurface)
|
|
9412
|
+
throw new Error(`Provider surface ${input.providerSurface} is not valid for Sumi.`);
|
|
9413
|
+
return Object.freeze({ ...SESSION_TOOL_ADAPTERS.sumi, projectScoped: true, envVar: undefined });
|
|
9414
|
+
}
|
|
9335
9415
|
if (input.tool === "opencode" && input.providerSurface) {
|
|
9336
9416
|
if (input.providerSurface !== "opencode-config-instructions" && input.providerSurface !== "opencode-agents-md") {
|
|
9337
9417
|
throw new Error(`Provider surface ${input.providerSurface} is not valid for OpenCode.`);
|
|
@@ -9373,7 +9453,7 @@ function adapterFor(input) {
|
|
|
9373
9453
|
}
|
|
9374
9454
|
if (input.tool !== "codewith")
|
|
9375
9455
|
return SESSION_TOOL_ADAPTERS[input.tool];
|
|
9376
|
-
const gatedNativeImports = input.codewithNativeImports
|
|
9456
|
+
const gatedNativeImports = input.codewithNativeImports ?? (process.env[CODEWITH_NATIVE_IMPORTS_ENV] === "1" || process.env[CODEWITH_NATIVE_IMPORTS_ENV] === "true");
|
|
9377
9457
|
return gatedNativeImports ? CODEWITH_NATIVE_ADAPTER : CODEWITH_FLATTENED_ADAPTER;
|
|
9378
9458
|
}
|
|
9379
9459
|
function getHomeDir() {
|
|
@@ -9424,6 +9504,13 @@ function assertSafeTargetRoot(targetHome) {
|
|
|
9424
9504
|
return normalized;
|
|
9425
9505
|
}
|
|
9426
9506
|
function resolveRenderTarget(input, adapter) {
|
|
9507
|
+
if (input.tool === "sumi" && !input.projectRoot && !input.targetHome?.trim()) {
|
|
9508
|
+
return {
|
|
9509
|
+
targetHome: defaultTargetHome(input.tool, input.profile, input.sessionId),
|
|
9510
|
+
targetKind: "blocked",
|
|
9511
|
+
blockers: ["Sumi requires --target-home from `sumi debug paths config`, or --project-root for repository AGENTS.md."]
|
|
9512
|
+
};
|
|
9513
|
+
}
|
|
9427
9514
|
if (adapter.projectScoped) {
|
|
9428
9515
|
if (!input.projectRoot) {
|
|
9429
9516
|
const label = input.tool === "cursor" ? "Cursor rules" : input.tool === "antigravity" ? "Antigravity rules" : `${adapter.description.split(".")[0]} files`;
|
|
@@ -9525,6 +9612,14 @@ function planSessionRender(input) {
|
|
|
9525
9612
|
const normalized = normalizeSources(input.sources, input.tool, allowEmptySources);
|
|
9526
9613
|
const composed = composeSources(normalized.sources, input.tool);
|
|
9527
9614
|
const orderedSources = composed.sources;
|
|
9615
|
+
if (input.tool === "sumi") {
|
|
9616
|
+
for (const source of orderedSources) {
|
|
9617
|
+
const activation = source.metadata?.["activation"];
|
|
9618
|
+
if (source.globs?.length || source.resolvedRules.some((rule) => rule.globs?.length) || activation?.mode && activation.mode !== "always") {
|
|
9619
|
+
throw new Error(`Sumi AGENTS.md cannot preserve conditional activation for source ${source.id}; compile a profile with an explicit supported fallback.`);
|
|
9620
|
+
}
|
|
9621
|
+
}
|
|
9622
|
+
}
|
|
9528
9623
|
const skippedSources = [
|
|
9529
9624
|
...input.skippedSources ?? [],
|
|
9530
9625
|
...normalized.skipped,
|
|
@@ -9568,6 +9663,7 @@ function planSessionRender(input) {
|
|
|
9568
9663
|
adapterMode: adapter.mode,
|
|
9569
9664
|
profile: input.profile,
|
|
9570
9665
|
sessionId: input.sessionId ?? null,
|
|
9666
|
+
...input.refreshSelector ? { refreshSelector: input.refreshSelector } : {},
|
|
9571
9667
|
targetHome,
|
|
9572
9668
|
targetKind,
|
|
9573
9669
|
targetOwner,
|
|
@@ -9741,8 +9837,9 @@ function selectProfileConfigsForSessionRender(configs, tool) {
|
|
|
9741
9837
|
skippedSources.push(skippedProfileConfig(config, ["opencode"], "provider settings belong to OpenCode"));
|
|
9742
9838
|
continue;
|
|
9743
9839
|
}
|
|
9744
|
-
|
|
9745
|
-
|
|
9840
|
+
const sourceRejection = instructionSourceRejection(config);
|
|
9841
|
+
if (sourceRejection) {
|
|
9842
|
+
skippedSources.push(skippedProfileConfig(config, [], sourceRejection));
|
|
9746
9843
|
continue;
|
|
9747
9844
|
}
|
|
9748
9845
|
const output = config.outputs.find((candidate) => candidate.agent === tool);
|
|
@@ -9750,7 +9847,7 @@ function selectProfileConfigsForSessionRender(configs, tool) {
|
|
|
9750
9847
|
skippedSources.push(skippedProfileConfig(config, [config.agent], "rule targets a different provider"));
|
|
9751
9848
|
continue;
|
|
9752
9849
|
}
|
|
9753
|
-
const selectedContent = output ? applyTransform(config, output, { configs }) : config.content;
|
|
9850
|
+
const selectedContent = output ? applyTransform(config, output, { configs: [config] }) : config.content;
|
|
9754
9851
|
sources.push({
|
|
9755
9852
|
config,
|
|
9756
9853
|
source: sourceFromConfig({ ...config, content: selectedContent }, sources.length)
|
|
@@ -10159,6 +10256,7 @@ var init_session_render = __esm(() => {
|
|
|
10159
10256
|
"codex",
|
|
10160
10257
|
"cursor",
|
|
10161
10258
|
"opencode",
|
|
10259
|
+
"sumi",
|
|
10162
10260
|
"codewith",
|
|
10163
10261
|
"qwen",
|
|
10164
10262
|
"aicopilot",
|
|
@@ -10247,6 +10345,15 @@ var init_session_render = __esm(() => {
|
|
|
10247
10345
|
description: "OpenCode AGENTS.md plus opencode.json instructions pointing at managed fragments.",
|
|
10248
10346
|
providerSurface: "legacy-dual"
|
|
10249
10347
|
},
|
|
10348
|
+
sumi: {
|
|
10349
|
+
tool: "sumi",
|
|
10350
|
+
mode: "flattened-markdown",
|
|
10351
|
+
indexFile: "AGENTS.md",
|
|
10352
|
+
managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
|
|
10353
|
+
envVar: "SUMI_CONFIG_DIR",
|
|
10354
|
+
nativeImports: false,
|
|
10355
|
+
description: "Sumi native AGENTS.md in the explicit resolved config directory or project root."
|
|
10356
|
+
},
|
|
10250
10357
|
aicopilot: {
|
|
10251
10358
|
tool: "aicopilot",
|
|
10252
10359
|
mode: "flattened-markdown",
|
|
@@ -10535,12 +10642,22 @@ function compileInstructionGraph(input) {
|
|
|
10535
10642
|
const selector = binding.providers?.find((entry) => entry.provider === input.context.provider || entry.provider === "global");
|
|
10536
10643
|
if (binding.providers?.length && !selector)
|
|
10537
10644
|
continue;
|
|
10645
|
+
const output = config.outputs.find((entry) => entry.agent === input.context.provider);
|
|
10646
|
+
if (!binding.providers?.length && config.agent !== "global" && config.agent !== input.context.provider && !output) {
|
|
10647
|
+
diagnostics.push({ severity: "info", code: "SOURCE_PROVIDER_NOT_SELECTED", config_id: configId, message: `${config.slug} targets ${config.agent}, not ${input.context.provider}.` });
|
|
10648
|
+
continue;
|
|
10649
|
+
}
|
|
10538
10650
|
if (selector?.version_range && !providerVersionSatisfies(input.context.provider_version, selector.version_range)) {
|
|
10539
10651
|
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
10652
|
continue;
|
|
10541
10653
|
}
|
|
10542
10654
|
if (!activationMatches(binding.activation, config, input.context))
|
|
10543
10655
|
continue;
|
|
10656
|
+
const rejection = instructionSourceRejection(config);
|
|
10657
|
+
if (rejection) {
|
|
10658
|
+
diagnostics.push({ severity: !binding.required && binding.fallback === "omit" ? "warning" : "error", code: "SOURCE_NOT_INSTRUCTION", config_id: configId, message: `${config.slug}: ${rejection}.` });
|
|
10659
|
+
continue;
|
|
10660
|
+
}
|
|
10544
10661
|
if (capability2.activation_modes.includes(binding.activation.mode)) {
|
|
10545
10662
|
selected.add(configId);
|
|
10546
10663
|
effective.set(configId, binding.activation);
|
|
@@ -10615,7 +10732,9 @@ function compileInstructionGraph(input) {
|
|
|
10615
10732
|
assertExactOnce(units, artifacts);
|
|
10616
10733
|
const sources = units.map((unit, index) => {
|
|
10617
10734
|
const config = configs.get(unit.config_id);
|
|
10618
|
-
|
|
10735
|
+
const output = config.outputs.find((entry) => entry.agent === input.context.provider);
|
|
10736
|
+
const content = output ? applyTransform(config, output, { configs: [config] }) : config.content;
|
|
10737
|
+
return sourceForUnit({ ...config, content }, unit, index);
|
|
10619
10738
|
});
|
|
10620
10739
|
const planWithoutHash = {
|
|
10621
10740
|
schema: INSTRUCTION_GRAPH_PLAN_SCHEMA,
|
|
@@ -10843,6 +10962,7 @@ var init_instruction_graph = __esm(() => {
|
|
|
10843
10962
|
init_types();
|
|
10844
10963
|
init_session_render();
|
|
10845
10964
|
init_asset_plan();
|
|
10965
|
+
init_transforms();
|
|
10846
10966
|
COMMON_FALLBACKS = ["fail", "flatten", "promote-always", "omit"];
|
|
10847
10967
|
DEFAULT_PROVIDER_CAPABILITIES = Object.freeze({
|
|
10848
10968
|
claude: capability("claude", ">=1.0.0", "native-import", "CLAUDE.md @ imports plus path-gated rules", {
|
|
@@ -10854,6 +10974,7 @@ var init_instruction_graph = __esm(() => {
|
|
|
10854
10974
|
codex: capability("codex", ">=0.1.0", "flattened", "AGENTS.md", { asset_surface: "cli" }),
|
|
10855
10975
|
cursor: capability("cursor", ">=1.0.0", "cursor-rule", ".cursor/rules/*.mdc", { activation_modes: ["always", "glob"], conditional_artifacts: true, asset_surface: "ide" }),
|
|
10856
10976
|
opencode: capability("opencode", ">=1.0.0", "managed-fragment", "opencode.json instructions", { provider_variant: "v1-instructions", session_surface: "opencode-config-instructions" }),
|
|
10977
|
+
sumi: capability("sumi", ">=0.2.22 <0.3.0", "flattened", "AGENTS.md"),
|
|
10857
10978
|
codewith: capability("codewith", ">=0.1.0", "flattened", "CODEWITH.md"),
|
|
10858
10979
|
qwen: capability("qwen", ">=0.1.0", "flattened", "QWEN.md"),
|
|
10859
10980
|
aicopilot: capability("aicopilot", ">=0.1.0", "flattened", "AICOPILOT.md"),
|
|
@@ -13464,6 +13585,7 @@ function listConfigSummariesPage(filter = {}, options = {}, db) {
|
|
|
13464
13585
|
})), total, normalized);
|
|
13465
13586
|
}
|
|
13466
13587
|
function updateConfig(idOrSlug, input, db) {
|
|
13588
|
+
validateExpectedConfigVersion(input.expected_version);
|
|
13467
13589
|
const d = db || getDatabase();
|
|
13468
13590
|
const existing = getConfig(idOrSlug, d);
|
|
13469
13591
|
const ts = now();
|
|
@@ -13519,7 +13641,15 @@ function updateConfig(idOrSlug, input, db) {
|
|
|
13519
13641
|
}
|
|
13520
13642
|
return d.transaction(() => {
|
|
13521
13643
|
params.push(existing.id);
|
|
13522
|
-
|
|
13644
|
+
const condition = input.expected_version === undefined ? "" : " AND version = ?";
|
|
13645
|
+
if (input.expected_version !== undefined)
|
|
13646
|
+
params.push(input.expected_version);
|
|
13647
|
+
const result = d.run(`UPDATE configs SET ${updates.join(", ")} WHERE id = ?${condition}`, params);
|
|
13648
|
+
if (result.changes !== 1) {
|
|
13649
|
+
if (input.expected_version !== undefined)
|
|
13650
|
+
throw new ConfigVersionConflictError(existing.id, input.expected_version);
|
|
13651
|
+
throw new ConfigNotFoundError(existing.id);
|
|
13652
|
+
}
|
|
13523
13653
|
const updated = getConfigById(existing.id, d);
|
|
13524
13654
|
createSnapshot(updated.id, updated.content, updated.version, d);
|
|
13525
13655
|
return updated;
|
|
@@ -14418,7 +14548,9 @@ class CloudConfigStore {
|
|
|
14418
14548
|
return data.config;
|
|
14419
14549
|
}
|
|
14420
14550
|
async updateConfig(idOrSlug, input) {
|
|
14421
|
-
|
|
14551
|
+
validateExpectedConfigVersion(input.expected_version);
|
|
14552
|
+
const conditional = input.expected_version !== undefined;
|
|
14553
|
+
const { data } = await this.request(conditional ? "POST" : "PATCH", `/configs/${encodeURIComponent(idOrSlug)}${conditional ? "/conditional-update" : ""}`, input);
|
|
14422
14554
|
return data.config;
|
|
14423
14555
|
}
|
|
14424
14556
|
async deleteConfig(idOrSlug) {
|
|
@@ -14495,7 +14627,7 @@ class CloudConfigStore {
|
|
|
14495
14627
|
throw new ProfileNotFoundError(idOrSlug);
|
|
14496
14628
|
return parseBoundedOrLegacyPage(data.configs, data.profile.configs, normalized, "profile membership");
|
|
14497
14629
|
}
|
|
14498
|
-
async getProfileConfigBindings(idOrSlug) {
|
|
14630
|
+
async getProfileConfigBindings(idOrSlug, options = {}) {
|
|
14499
14631
|
let routeMissing = false;
|
|
14500
14632
|
const bindings = await aggregateBoundedCollection("profile config bindings", async (cursor) => {
|
|
14501
14633
|
const normalized = normalizeBoundedReadOptions({ limit: 100, cursor });
|
|
@@ -14508,6 +14640,8 @@ class CloudConfigStore {
|
|
|
14508
14640
|
}, (binding) => `${binding.profile_id}\x00${binding.config_id}`);
|
|
14509
14641
|
if (!routeMissing)
|
|
14510
14642
|
return bindings;
|
|
14643
|
+
if (options.requireExplicit)
|
|
14644
|
+
throw new Error("HOSTED_BINDINGS_REQUIRED: profile config binding route is unavailable; refusing legacy activation fallback.");
|
|
14511
14645
|
const profile = await this.getProfile(idOrSlug);
|
|
14512
14646
|
const configs = await this.getProfileConfigs(idOrSlug);
|
|
14513
14647
|
return configs.map((config, sort_order) => ({
|
|
@@ -14572,7 +14706,7 @@ class CloudConfigStore {
|
|
|
14572
14706
|
async removeConfigFromProfile(profileIdOrSlug, configId) {
|
|
14573
14707
|
await this.request("DELETE", `/profiles/${encodeURIComponent(profileIdOrSlug)}/configs/${encodeURIComponent(configId)}`, undefined, { allow404: true });
|
|
14574
14708
|
}
|
|
14575
|
-
async getProfileAssetBindings(profileIdOrSlug) {
|
|
14709
|
+
async getProfileAssetBindings(profileIdOrSlug, options = {}) {
|
|
14576
14710
|
let routeMissing = false;
|
|
14577
14711
|
const assets = await aggregateBoundedCollection("profile asset bindings", async (cursor) => {
|
|
14578
14712
|
const normalized = normalizeBoundedReadOptions({ limit: 100, cursor });
|
|
@@ -14585,6 +14719,8 @@ class CloudConfigStore {
|
|
|
14585
14719
|
}, (asset) => `${asset.profile_id}\x00${asset.binding.assetKey}`);
|
|
14586
14720
|
if (!routeMissing)
|
|
14587
14721
|
return assets;
|
|
14722
|
+
if (options.requireExplicit)
|
|
14723
|
+
throw new Error("HOSTED_ASSETS_REQUIRED: profile asset binding route is unavailable; refusing an empty asset fallback.");
|
|
14588
14724
|
await this.getProfile(profileIdOrSlug);
|
|
14589
14725
|
return [];
|
|
14590
14726
|
}
|
|
@@ -16334,7 +16470,6 @@ import {
|
|
|
16334
16470
|
statSync as statSync5
|
|
16335
16471
|
} from "fs";
|
|
16336
16472
|
import { dirname as dirname10, isAbsolute as isAbsolute7, join as join17, parse as parse5, relative as relative6, resolve as resolve11 } from "path";
|
|
16337
|
-
|
|
16338
16473
|
class SessionApplyError extends Error {
|
|
16339
16474
|
constructor(message) {
|
|
16340
16475
|
super(message);
|
|
@@ -16356,14 +16491,20 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
16356
16491
|
const targetHome = assertSafeTargetHome(plan.targetHome);
|
|
16357
16492
|
assertClaudeAuthorityStillClear(plan, targetHome, options.ownedClaudeAuthorities);
|
|
16358
16493
|
const payloadFiles = [...plan.files, ...plan.assetFiles ?? []];
|
|
16359
|
-
const files = [...payloadFiles, plan.manifestFile];
|
|
16360
16494
|
const manifestPath = resolvePlannedFilePath(plan, plan.manifestFile, targetHome);
|
|
16495
|
+
assertManifestPrecondition(manifestPath, targetHome, options.expectedManifestSha256);
|
|
16361
16496
|
const previousManifest = readPreviousManifest(manifestPath);
|
|
16362
16497
|
const previousHashes = previousManifest ? new Map(previousManifest.files.map((file) => [file.relativePath, file.sha256])) : new Map;
|
|
16498
|
+
const adoptions = validateFileAdoptions(plan, targetHome, previousHashes, options);
|
|
16499
|
+
const adoptedHashes = new Map(adoptions.map((entry) => [entry.relativePath, entry.preimageSha256]));
|
|
16500
|
+
const reconciliations = validateFileReconciliations(plan, targetHome, previousManifest, options);
|
|
16501
|
+
const reconciledHashes = new Map(reconciliations.map((entry) => [entry.relativePath, entry.preimageSha256]));
|
|
16502
|
+
const manifestFile = manifestWithFileProvenance(plan, previousManifest, adoptions, reconciliations);
|
|
16503
|
+
const files = [...payloadFiles, manifestFile];
|
|
16363
16504
|
const currentRelativePaths = new Set(files.map((file) => file.relativePath));
|
|
16364
16505
|
const drift = checkSessionRenderDrift(targetHome, manifestPath);
|
|
16365
16506
|
const results = [
|
|
16366
|
-
...files.map((file) => planFileResult(plan, file, targetHome, previousHashes, previousManifest, options)),
|
|
16507
|
+
...files.map((file) => planFileResult(plan, file, targetHome, previousHashes, previousManifest, options, adoptedHashes, reconciledHashes)),
|
|
16367
16508
|
...planStaleFileResults(plan, targetHome, previousManifest, currentRelativePaths, options)
|
|
16368
16509
|
];
|
|
16369
16510
|
assertNotSilentManagedWipeout(plan, results);
|
|
@@ -16386,7 +16527,9 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
16386
16527
|
skippedSources: plan.manifest.skippedSources,
|
|
16387
16528
|
files: results,
|
|
16388
16529
|
conflicts,
|
|
16389
|
-
drift
|
|
16530
|
+
drift,
|
|
16531
|
+
adoptions,
|
|
16532
|
+
reconciliations
|
|
16390
16533
|
};
|
|
16391
16534
|
}
|
|
16392
16535
|
let snapshotPath = null;
|
|
@@ -16399,10 +16542,12 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
16399
16542
|
if (!options.dryRun) {
|
|
16400
16543
|
const allowPortableFallback = coordination === null;
|
|
16401
16544
|
const forcePortableFileOps = options.test_hooks?.force_portable_file_ops ?? false;
|
|
16545
|
+
assertManifestPrecondition(manifestPath, targetHome, options.expectedManifestSha256);
|
|
16402
16546
|
ensureSessionTargetHome(targetHome);
|
|
16403
|
-
rollback = writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps);
|
|
16547
|
+
rollback = writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps, adoptions, reconciliations);
|
|
16404
16548
|
snapshotPath = rollback.snapshotPath;
|
|
16405
16549
|
options.test_hooks?.before_apply_writes?.({ plan, results });
|
|
16550
|
+
assertManifestPrecondition(manifestPath, targetHome, options.expectedManifestSha256);
|
|
16406
16551
|
const resultsByPath = new Map(results.map((result) => [result.path, result]));
|
|
16407
16552
|
for (const file of payloadFiles) {
|
|
16408
16553
|
applyPlannedFile(plan, file, targetHome, resultsByPath, coordination, allowPortableFallback, forcePortableFileOps);
|
|
@@ -16422,7 +16567,7 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
16422
16567
|
});
|
|
16423
16568
|
coordination?.assert_held();
|
|
16424
16569
|
}
|
|
16425
|
-
applyPlannedFile(plan,
|
|
16570
|
+
applyPlannedFile(plan, manifestFile, targetHome, resultsByPath, coordination, allowPortableFallback, forcePortableFileOps);
|
|
16426
16571
|
}
|
|
16427
16572
|
return {
|
|
16428
16573
|
dryRun: options.dryRun ?? false,
|
|
@@ -16436,8 +16581,170 @@ function applySessionRenderUnlocked(plan, options, coordination) {
|
|
|
16436
16581
|
skippedSources: plan.manifest.skippedSources,
|
|
16437
16582
|
files: results,
|
|
16438
16583
|
conflicts,
|
|
16439
|
-
drift
|
|
16584
|
+
drift,
|
|
16585
|
+
adoptions,
|
|
16586
|
+
reconciliations
|
|
16587
|
+
};
|
|
16588
|
+
}
|
|
16589
|
+
function assertManifestPrecondition(path, targetHome, expected) {
|
|
16590
|
+
if (expected === undefined)
|
|
16591
|
+
return;
|
|
16592
|
+
if (!/^[a-f0-9]{64}$/.test(expected)) {
|
|
16593
|
+
throw new SessionApplyError("Expected session manifest SHA-256 must contain 64 lowercase hexadecimal characters.");
|
|
16594
|
+
}
|
|
16595
|
+
if (currentSessionFileHash(path, targetHome) !== expected) {
|
|
16596
|
+
throw new SessionApplyError("Session manifest SHA-256 precondition failed; reload the manifest and resolve a new plan.");
|
|
16597
|
+
}
|
|
16598
|
+
}
|
|
16599
|
+
function validateFileAdoptions(plan, targetHome, previousHashes, options) {
|
|
16600
|
+
const requests = options.adoptFiles ?? [];
|
|
16601
|
+
if (!Array.isArray(requests))
|
|
16602
|
+
throw new SessionApplyError("Session file adoptions must be an array.");
|
|
16603
|
+
if (requests.length > 0 && options.force) {
|
|
16604
|
+
throw new SessionApplyError("Exact file adoption cannot be combined with force.");
|
|
16605
|
+
}
|
|
16606
|
+
const seen = new Set;
|
|
16607
|
+
return requests.map((request) => {
|
|
16608
|
+
if (!request || typeof request.relativePath !== "string" || typeof request.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(request.sha256)) {
|
|
16609
|
+
throw new SessionApplyError("Each file adoption requires a plan-relative path and a 64-character lowercase SHA-256.");
|
|
16610
|
+
}
|
|
16611
|
+
if (seen.has(request.relativePath)) {
|
|
16612
|
+
throw new SessionApplyError(`Duplicate file adoption target: ${request.relativePath}`);
|
|
16613
|
+
}
|
|
16614
|
+
seen.add(request.relativePath);
|
|
16615
|
+
const candidates = plan.files.filter((file2) => file2.relativePath === request.relativePath);
|
|
16616
|
+
if (candidates.length !== 1 || candidates[0].role === "manifest") {
|
|
16617
|
+
throw new SessionApplyError(`File adoption target is not a unique planned instruction output: ${request.relativePath}`);
|
|
16618
|
+
}
|
|
16619
|
+
const file = candidates[0];
|
|
16620
|
+
const path = resolvePlannedFilePath(plan, file, targetHome);
|
|
16621
|
+
if (previousHashes.has(file.relativePath) || sessionRenderOwnsPath(path)) {
|
|
16622
|
+
throw new SessionApplyError(`File adoption target is already managed: ${request.relativePath}`);
|
|
16623
|
+
}
|
|
16624
|
+
const observedSha256 = readExactFilePreimage(path, targetHome, request, "adoption");
|
|
16625
|
+
return {
|
|
16626
|
+
path,
|
|
16627
|
+
relativePath: file.relativePath,
|
|
16628
|
+
preimageSha256: observedSha256,
|
|
16629
|
+
renderedSha256: file.sha256,
|
|
16630
|
+
sourceIds: [...file.sourceIds]
|
|
16631
|
+
};
|
|
16632
|
+
});
|
|
16633
|
+
}
|
|
16634
|
+
function readExactFilePreimage(path, targetHome, request, operation) {
|
|
16635
|
+
if (currentSessionFileHash(path, targetHome) === null) {
|
|
16636
|
+
throw new SessionApplyError(`File ${operation} target does not exist: ${request.relativePath}`);
|
|
16637
|
+
}
|
|
16638
|
+
const bytes = readFileSync14(path);
|
|
16639
|
+
if (!bytes.equals(Buffer.from(bytes.toString("utf8"), "utf8"))) {
|
|
16640
|
+
throw new SessionApplyError(`File ${operation} target must contain losslessly restorable UTF-8: ${request.relativePath}`);
|
|
16641
|
+
}
|
|
16642
|
+
const observedSha256 = createHash10("sha256").update(bytes).digest("hex");
|
|
16643
|
+
if (observedSha256 !== request.sha256) {
|
|
16644
|
+
throw new SessionApplyError(`File ${operation} SHA-256 precondition failed: ${request.relativePath}`);
|
|
16645
|
+
}
|
|
16646
|
+
return observedSha256;
|
|
16647
|
+
}
|
|
16648
|
+
function validateFileReconciliations(plan, targetHome, previousManifest, options) {
|
|
16649
|
+
const requests = options.reconcileFiles ?? [];
|
|
16650
|
+
if (!Array.isArray(requests))
|
|
16651
|
+
throw new SessionApplyError("Session file reconciliations must be an array.");
|
|
16652
|
+
if (requests.length === 0)
|
|
16653
|
+
return [];
|
|
16654
|
+
if (options.force)
|
|
16655
|
+
throw new SessionApplyError("Exact file reconciliation cannot be combined with force.");
|
|
16656
|
+
if (options.expectedManifestSha256 === undefined) {
|
|
16657
|
+
throw new SessionApplyError("Exact file reconciliation requires an expected manifest SHA-256 precondition.");
|
|
16658
|
+
}
|
|
16659
|
+
if (!previousManifest || previousManifest.targetOwner?.writer?.id !== SESSION_RENDERER_OWNER_ID || previousManifest.tool !== plan.tool || previousManifest.targetHome !== targetHome) {
|
|
16660
|
+
throw new SessionApplyError("File reconciliation requires this renderer's manifest for the same tool and target home.");
|
|
16661
|
+
}
|
|
16662
|
+
const seen = new Set;
|
|
16663
|
+
return requests.map((request) => {
|
|
16664
|
+
if (!request || typeof request.relativePath !== "string" || typeof request.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(request.sha256)) {
|
|
16665
|
+
throw new SessionApplyError("Each file reconciliation requires a plan-relative path and a 64-character lowercase SHA-256.");
|
|
16666
|
+
}
|
|
16667
|
+
if (seen.has(request.relativePath)) {
|
|
16668
|
+
throw new SessionApplyError(`Duplicate file reconciliation target: ${request.relativePath}`);
|
|
16669
|
+
}
|
|
16670
|
+
seen.add(request.relativePath);
|
|
16671
|
+
const candidates = plan.files.filter((file2) => file2.relativePath === request.relativePath);
|
|
16672
|
+
if (candidates.length !== 1 || candidates[0].role === "manifest") {
|
|
16673
|
+
throw new SessionApplyError(`File reconciliation target is not a unique planned instruction output: ${request.relativePath}`);
|
|
16674
|
+
}
|
|
16675
|
+
const file = candidates[0];
|
|
16676
|
+
const path = resolvePlannedFilePath(plan, file, targetHome);
|
|
16677
|
+
const owned = previousManifest.files.filter((entry) => entry.relativePath === file.relativePath);
|
|
16678
|
+
if (owned.length !== 1 || owned[0].path !== path || owned[0].role !== file.role || !/^[a-f0-9]{64}$/.test(owned[0].sha256)) {
|
|
16679
|
+
throw new SessionApplyError(`File reconciliation target is not owned by the observed manifest: ${request.relativePath}`);
|
|
16680
|
+
}
|
|
16681
|
+
const observedSha256 = readExactFilePreimage(path, targetHome, request, "reconciliation");
|
|
16682
|
+
if (observedSha256 === owned[0].sha256) {
|
|
16683
|
+
throw new SessionApplyError(`File reconciliation target has no managed drift: ${request.relativePath}`);
|
|
16684
|
+
}
|
|
16685
|
+
if (observedSha256 === file.sha256) {
|
|
16686
|
+
throw new SessionApplyError(`File reconciliation target already matches the planned output: ${request.relativePath}`);
|
|
16687
|
+
}
|
|
16688
|
+
return {
|
|
16689
|
+
path,
|
|
16690
|
+
relativePath: file.relativePath,
|
|
16691
|
+
preimageSha256: observedSha256,
|
|
16692
|
+
renderedSha256: file.sha256,
|
|
16693
|
+
previousManagedSha256: owned[0].sha256,
|
|
16694
|
+
sourceIds: [...file.sourceIds]
|
|
16695
|
+
};
|
|
16696
|
+
});
|
|
16697
|
+
}
|
|
16698
|
+
function manifestWithFileProvenance(plan, previousManifest, adoptions, reconciliations) {
|
|
16699
|
+
if (adoptions.length === 0 && previousManifest?.adoptions === undefined && reconciliations.length === 0 && previousManifest?.reconciliations === undefined)
|
|
16700
|
+
return plan.manifestFile;
|
|
16701
|
+
if (previousManifest?.adoptions !== undefined && !Array.isArray(previousManifest.adoptions)) {
|
|
16702
|
+
throw new SessionApplyError("Previous session manifest adoption provenance is invalid.");
|
|
16703
|
+
}
|
|
16704
|
+
const entries = new Map;
|
|
16705
|
+
for (const entry of [
|
|
16706
|
+
...previousManifest?.adoptions ?? [],
|
|
16707
|
+
...adoptions.map(({ path: _path, ...receipt }) => receipt)
|
|
16708
|
+
]) {
|
|
16709
|
+
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")) {
|
|
16710
|
+
throw new SessionApplyError("Previous session manifest adoption provenance is invalid.");
|
|
16711
|
+
}
|
|
16712
|
+
resolveManifestRelativePath(entry.relativePath, plan.targetHome);
|
|
16713
|
+
const previous = entries.get(entry.relativePath);
|
|
16714
|
+
if (previous && JSON.stringify(previous) !== JSON.stringify(entry)) {
|
|
16715
|
+
throw new SessionApplyError(`Conflicting session file adoption provenance: ${entry.relativePath}`);
|
|
16716
|
+
}
|
|
16717
|
+
entries.set(entry.relativePath, entry);
|
|
16718
|
+
}
|
|
16719
|
+
if (previousManifest?.reconciliations !== undefined && !Array.isArray(previousManifest.reconciliations)) {
|
|
16720
|
+
throw new SessionApplyError("Previous session manifest reconciliation provenance is invalid.");
|
|
16721
|
+
}
|
|
16722
|
+
const reconciledEntries = new Map;
|
|
16723
|
+
for (const entry of [
|
|
16724
|
+
...previousManifest?.reconciliations ?? [],
|
|
16725
|
+
...reconciliations.map(({ path: _path, ...receipt }) => receipt)
|
|
16726
|
+
]) {
|
|
16727
|
+
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")) {
|
|
16728
|
+
throw new SessionApplyError("Previous session manifest reconciliation provenance is invalid.");
|
|
16729
|
+
}
|
|
16730
|
+
resolveManifestRelativePath(entry.relativePath, plan.targetHome);
|
|
16731
|
+
const canonical = {
|
|
16732
|
+
relativePath: entry.relativePath,
|
|
16733
|
+
preimageSha256: entry.preimageSha256,
|
|
16734
|
+
renderedSha256: entry.renderedSha256,
|
|
16735
|
+
previousManagedSha256: entry.previousManagedSha256,
|
|
16736
|
+
sourceIds: [...entry.sourceIds]
|
|
16737
|
+
};
|
|
16738
|
+
reconciledEntries.set(JSON.stringify(canonical), canonical);
|
|
16739
|
+
}
|
|
16740
|
+
const manifest = {
|
|
16741
|
+
...plan.manifest,
|
|
16742
|
+
...adoptions.length > 0 || previousManifest?.adoptions !== undefined ? { adoptions: [...entries.values()] } : {},
|
|
16743
|
+
...reconciliations.length > 0 || previousManifest?.reconciliations !== undefined ? { reconciliations: [...reconciledEntries.values()] } : {}
|
|
16440
16744
|
};
|
|
16745
|
+
const content = `${JSON.stringify(manifest, null, 2)}
|
|
16746
|
+
`;
|
|
16747
|
+
return { ...plan.manifestFile, content, sha256: sha25610(content) };
|
|
16441
16748
|
}
|
|
16442
16749
|
function assertCursorAuthorityUnchanged(plan) {
|
|
16443
16750
|
if (plan.tool !== "cursor" || plan.targetKind === "blocked")
|
|
@@ -16910,12 +17217,29 @@ function resolveSnapshotFilePath(relativePath, recordedPath, targetHome) {
|
|
|
16910
17217
|
}
|
|
16911
17218
|
return path;
|
|
16912
17219
|
}
|
|
16913
|
-
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options) {
|
|
17220
|
+
function planFileResult(plan, file, targetHome, previousHashes, previousManifest, options, adoptedHashes, reconciledHashes) {
|
|
16914
17221
|
const target = resolvePlannedFilePath(plan, file, targetHome);
|
|
16915
17222
|
const previousContent = existsSync15(target) ? readFileSync14(target, "utf-8") : null;
|
|
16916
17223
|
const previousSha256 = previousContent === null ? null : sha25610(previousContent);
|
|
16917
17224
|
const previouslyManaged = isPreviouslyManaged(file, previousSha256, previousHashes, previousManifest);
|
|
16918
17225
|
const changed = previousContent !== file.content;
|
|
17226
|
+
const exactSha256 = adoptedHashes.get(file.relativePath) ?? reconciledHashes.get(file.relativePath);
|
|
17227
|
+
if (exactSha256 !== undefined) {
|
|
17228
|
+
const operation = adoptedHashes.has(file.relativePath) ? "adoption" : "reconciliation";
|
|
17229
|
+
if (previousSha256 !== exactSha256) {
|
|
17230
|
+
throw new SessionApplyError(`File ${operation} SHA-256 precondition failed: ${file.relativePath}`);
|
|
17231
|
+
}
|
|
17232
|
+
return {
|
|
17233
|
+
path: target,
|
|
17234
|
+
relativePath: file.relativePath,
|
|
17235
|
+
role: file.role,
|
|
17236
|
+
action: "update",
|
|
17237
|
+
changed,
|
|
17238
|
+
previousSha256,
|
|
17239
|
+
newSha256: file.sha256,
|
|
17240
|
+
reason: `exact observed SHA-256 ${operation}`
|
|
17241
|
+
};
|
|
17242
|
+
}
|
|
16919
17243
|
if (previousContent !== null && !options.force && !previouslyManaged) {
|
|
16920
17244
|
return {
|
|
16921
17245
|
path: target,
|
|
@@ -17135,9 +17459,13 @@ function requiredPreviousHash(result) {
|
|
|
17135
17459
|
}
|
|
17136
17460
|
return result.previousSha256;
|
|
17137
17461
|
}
|
|
17138
|
-
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps) {
|
|
17139
|
-
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").
|
|
17462
|
+
function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousManifest, coordination, allowPortableFallback, forcePortableFileOps, adoptions, reconciliations) {
|
|
17463
|
+
const existingFiles = results.filter((result) => result.action === "update" || result.action === "delete").map((result) => {
|
|
17464
|
+
assertExpectedSessionFileHash(result.path, targetHome, result.previousSha256);
|
|
17140
17465
|
const content = readFileSync14(result.path, "utf-8");
|
|
17466
|
+
if (sha25610(content) !== result.previousSha256) {
|
|
17467
|
+
throw new SessionApplyError(`Session snapshot preimage changed after planning: ${result.relativePath}`);
|
|
17468
|
+
}
|
|
17141
17469
|
return {
|
|
17142
17470
|
path: result.path,
|
|
17143
17471
|
relativePath: result.relativePath,
|
|
@@ -17178,13 +17506,19 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
|
|
|
17178
17506
|
manifestPath,
|
|
17179
17507
|
previousManifest,
|
|
17180
17508
|
files: existingFiles,
|
|
17181
|
-
afterFiles
|
|
17509
|
+
afterFiles,
|
|
17510
|
+
...adoptions.length > 0 ? { adoptions } : {},
|
|
17511
|
+
...reconciliations.length > 0 ? { reconciliations } : {}
|
|
17182
17512
|
};
|
|
17513
|
+
const snapshotContent = `${JSON.stringify(snapshot, null, 2)}
|
|
17514
|
+
`;
|
|
17515
|
+
if ((adoptions.length > 0 || reconciliations.length > 0) && Buffer.byteLength(snapshotContent, "utf8") > 32 * 1024 * 1024) {
|
|
17516
|
+
throw new SessionApplyError("Exact file preimage snapshot exceeds the 32 MiB restore limit.");
|
|
17517
|
+
}
|
|
17183
17518
|
coordination?.assert_held();
|
|
17184
17519
|
writeProjectContextCoordinatedFile({
|
|
17185
17520
|
path: snapshotPath,
|
|
17186
|
-
content:
|
|
17187
|
-
`,
|
|
17521
|
+
content: snapshotContent,
|
|
17188
17522
|
workspace_root: sessionRenderSnapshotWorkspaceRoot(targetHome),
|
|
17189
17523
|
default_mode: 384,
|
|
17190
17524
|
expected_hash: null,
|
|
@@ -17241,6 +17575,211 @@ function assertNoSymlinkAncestors3(path) {
|
|
|
17241
17575
|
function sha25610(content) {
|
|
17242
17576
|
return createHash10("sha256").update(content).digest("hex");
|
|
17243
17577
|
}
|
|
17578
|
+
// src/lib/session-refresh.ts
|
|
17579
|
+
init_zod();
|
|
17580
|
+
import { createHash as createHash11 } from "crypto";
|
|
17581
|
+
import { closeSync as closeSync4, constants as constants2, fstatSync as fstatSync4, lstatSync as lstatSync7, openSync as openSync4, readFileSync as readFileSync15 } from "fs";
|
|
17582
|
+
import { dirname as dirname11, join as join18, parse as parse6, resolve as resolve12 } from "path";
|
|
17583
|
+
|
|
17584
|
+
// src/lib/global-source-coverage.ts
|
|
17585
|
+
var GLOBAL_SOURCE_SLUG_PREFIX = "global-";
|
|
17586
|
+
function expectedGlobalSourceSlugs(registryConfigs) {
|
|
17587
|
+
return registryConfigs.filter((c) => c.slug.startsWith(GLOBAL_SOURCE_SLUG_PREFIX)).filter((c) => instructionSourceRejection(c) === null).map((c) => c.slug).sort();
|
|
17588
|
+
}
|
|
17589
|
+
function accountedGlobalSourceSlugs(manifest) {
|
|
17590
|
+
return [...manifest.sources, ...manifest.skippedSources].map((source) => source.id).filter((id) => id.startsWith(GLOBAL_SOURCE_SLUG_PREFIX));
|
|
17591
|
+
}
|
|
17592
|
+
function computeGlobalSourceCoverage(registryConfigs, configuredSlugs) {
|
|
17593
|
+
const expected = expectedGlobalSourceSlugs(registryConfigs);
|
|
17594
|
+
const expectedSet = new Set(expected);
|
|
17595
|
+
const configuredSet = new Set(configuredSlugs);
|
|
17596
|
+
const missing = expected.filter((slug2) => !configuredSet.has(slug2));
|
|
17597
|
+
const unexpected = [...configuredSet].filter((slug2) => slug2.startsWith(GLOBAL_SOURCE_SLUG_PREFIX) && !expectedSet.has(slug2)).sort();
|
|
17598
|
+
return {
|
|
17599
|
+
expectedSlugs: expected,
|
|
17600
|
+
excludedSources: registryConfigs.filter((config) => config.slug.startsWith(GLOBAL_SOURCE_SLUG_PREFIX)).flatMap((config) => {
|
|
17601
|
+
const reason = instructionSourceRejection(config);
|
|
17602
|
+
return reason ? [{ slug: config.slug, reason }] : [];
|
|
17603
|
+
}).sort((left, right) => left.slug.localeCompare(right.slug)),
|
|
17604
|
+
configuredSlugs: [...configuredSet].sort(),
|
|
17605
|
+
missingSlugs: missing,
|
|
17606
|
+
unexpectedSlugs: unexpected,
|
|
17607
|
+
complete: missing.length === 0
|
|
17608
|
+
};
|
|
17609
|
+
}
|
|
17610
|
+
|
|
17611
|
+
// src/lib/session-refresh.ts
|
|
17612
|
+
init_instruction_graph();
|
|
17613
|
+
init_project_context();
|
|
17614
|
+
init_session_render();
|
|
17615
|
+
import { basename as basename6 } from "path";
|
|
17616
|
+
var nonempty = exports_external.string().min(1).max(4096);
|
|
17617
|
+
var selectorSchema = exports_external.object({
|
|
17618
|
+
schema: exports_external.literal("hasna.instructions.hosted-profile-selector/v1"),
|
|
17619
|
+
authority: nonempty,
|
|
17620
|
+
profileId: nonempty,
|
|
17621
|
+
providerVersion: nonempty,
|
|
17622
|
+
providerVariant: nonempty.optional(),
|
|
17623
|
+
model: nonempty.optional(),
|
|
17624
|
+
path: nonempty.optional(),
|
|
17625
|
+
manual: exports_external.array(nonempty).max(256),
|
|
17626
|
+
codewithNativeImports: exports_external.boolean(),
|
|
17627
|
+
allowEmptySources: exports_external.boolean(),
|
|
17628
|
+
stationProfile: exports_external.boolean(),
|
|
17629
|
+
checkGlobalCoverage: exports_external.boolean(),
|
|
17630
|
+
assetSurface: nonempty.optional(),
|
|
17631
|
+
assetScope: exports_external.enum(["global", "project", "session"]).optional()
|
|
17632
|
+
}).strict();
|
|
17633
|
+
function authority(value) {
|
|
17634
|
+
const url = new URL(value);
|
|
17635
|
+
if (url.username || url.password || url.search || url.hash || !["https:", "http:"].includes(url.protocol)) {
|
|
17636
|
+
throw new Error("SESSION_REFRESH_AUTHORITY_INVALID: expected a credential-free Instructions API authority.");
|
|
17637
|
+
}
|
|
17638
|
+
return url.toString().replace(/\/$/, "");
|
|
17639
|
+
}
|
|
17640
|
+
function normalizeSessionHostedProfileSelector(value) {
|
|
17641
|
+
const parsed = selectorSchema.parse(value);
|
|
17642
|
+
return { ...parsed, authority: authority(parsed.authority) };
|
|
17643
|
+
}
|
|
17644
|
+
function assertHostedProfileBindings(profileId, configs, bindings) {
|
|
17645
|
+
const configIds = new Set(configs.map((config) => config.id));
|
|
17646
|
+
const bindingIds = new Set(bindings.map((binding) => binding.config_id));
|
|
17647
|
+
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))) {
|
|
17648
|
+
throw new Error("HOSTED_PROFILE_BINDINGS_INCOMPLETE: exact one-to-one config membership and explicit profile bindings are required; refusing legacy activation fallback.");
|
|
17649
|
+
}
|
|
17650
|
+
for (const row of bindings) {
|
|
17651
|
+
const raw = row.binding;
|
|
17652
|
+
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.");
|
|
17653
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
17654
|
+
throw invalid();
|
|
17655
|
+
const record = raw;
|
|
17656
|
+
if (!["schema", "activation", "required", "fallback"].every((key) => Object.hasOwn(record, key)) || typeof record["required"] !== "boolean")
|
|
17657
|
+
throw invalid();
|
|
17658
|
+
try {
|
|
17659
|
+
normalizeProfileConfigBinding(record);
|
|
17660
|
+
} catch {
|
|
17661
|
+
throw invalid();
|
|
17662
|
+
}
|
|
17663
|
+
}
|
|
17664
|
+
}
|
|
17665
|
+
function readManagedManifest(targetHome) {
|
|
17666
|
+
const path = join18(targetHome, SESSION_RENDER_MANIFEST_RELATIVE_PATH);
|
|
17667
|
+
let ancestor = dirname11(path);
|
|
17668
|
+
while (true) {
|
|
17669
|
+
const stat = lstatSync7(ancestor);
|
|
17670
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
17671
|
+
throw new Error("SESSION_REFRESH_PATH_INVALID: managed target uses a symlink or non-directory ancestor.");
|
|
17672
|
+
const parent = dirname11(ancestor);
|
|
17673
|
+
if (parent === ancestor)
|
|
17674
|
+
break;
|
|
17675
|
+
ancestor = parent;
|
|
17676
|
+
}
|
|
17677
|
+
const fd = openSync4(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
|
|
17678
|
+
let raw;
|
|
17679
|
+
try {
|
|
17680
|
+
const stat = fstatSync4(fd);
|
|
17681
|
+
if (!stat.isFile() || stat.size > SESSION_MANAGED_INPUT_MAX_BYTES)
|
|
17682
|
+
throw new Error("SESSION_REFRESH_MANIFEST_INVALID: expected a bounded regular manifest file.");
|
|
17683
|
+
raw = readFileSync15(fd);
|
|
17684
|
+
if (raw.byteLength > SESSION_MANAGED_INPUT_MAX_BYTES)
|
|
17685
|
+
throw new Error("SESSION_REFRESH_MANIFEST_INVALID: manifest grew beyond the read limit.");
|
|
17686
|
+
} finally {
|
|
17687
|
+
closeSync4(fd);
|
|
17688
|
+
}
|
|
17689
|
+
const manifest = JSON.parse(raw.toString("utf8"));
|
|
17690
|
+
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) {
|
|
17691
|
+
throw new Error("SESSION_REFRESH_MANIFEST_INVALID: target is not an owned hosted profile render; apply a hosted --compile-profile first.");
|
|
17692
|
+
}
|
|
17693
|
+
manifest.refreshSelector = normalizeSessionHostedProfileSelector(manifest.refreshSelector);
|
|
17694
|
+
return { manifest, sha256: createHash11("sha256").update(raw).digest("hex") };
|
|
17695
|
+
}
|
|
17696
|
+
async function refreshSessionRender(input) {
|
|
17697
|
+
const { store } = input;
|
|
17698
|
+
if (store.mode !== "api" || !store.v1BaseUrl)
|
|
17699
|
+
throw new Error("SESSION_REFRESH_HOSTED_REQUIRED: refresh requires the configured hosted Instructions API.");
|
|
17700
|
+
const targetHome = resolve12(input.targetHome);
|
|
17701
|
+
if (targetHome === parse6(targetHome).root)
|
|
17702
|
+
throw new Error("SESSION_REFRESH_PATH_INVALID: filesystem root is not a managed target.");
|
|
17703
|
+
const previous = readManagedManifest(targetHome);
|
|
17704
|
+
const selector = previous.manifest.refreshSelector;
|
|
17705
|
+
if (authority(store.v1BaseUrl) !== selector.authority)
|
|
17706
|
+
throw new Error("SESSION_REFRESH_AUTHORITY_MISMATCH: configured API differs from the recorded profile authority.");
|
|
17707
|
+
const profile = await store.getProfile(selector.profileId);
|
|
17708
|
+
if (profile.id !== selector.profileId)
|
|
17709
|
+
throw new Error("SESSION_REFRESH_PROFILE_MISMATCH: hosted profile identity changed.");
|
|
17710
|
+
const [configs, bindings, assetBindings] = await Promise.all([
|
|
17711
|
+
store.getProfileConfigs(profile.id),
|
|
17712
|
+
store.getProfileConfigBindings(profile.id, { requireExplicit: true }),
|
|
17713
|
+
store.getProfileAssetBindings(profile.id, { requireExplicit: true })
|
|
17714
|
+
]);
|
|
17715
|
+
assertHostedProfileBindings(profile.id, configs, bindings);
|
|
17716
|
+
if (configs.length > 2048 || bindings.length > 8192 || assetBindings.length > 1024)
|
|
17717
|
+
throw new Error("SESSION_REFRESH_INPUT_LIMIT: hosted profile exceeds refresh bounds.");
|
|
17718
|
+
const totalBytes = configs.reduce((sum, config) => sum + Buffer.byteLength(config.content), 0);
|
|
17719
|
+
if (totalBytes > SESSION_MANAGED_INPUT_MAX_BYTES)
|
|
17720
|
+
throw new Error("SESSION_REFRESH_INPUT_LIMIT: hosted instruction payload exceeds refresh byte bounds.");
|
|
17721
|
+
const assetConfigs = await Promise.all([...new Set(assetBindings.map((binding) => binding.source_config_id))].map((id) => store.getConfigById(id)));
|
|
17722
|
+
if (assetConfigs.reduce((sum, config) => sum + Buffer.byteLength(config.content), totalBytes) > SESSION_MANAGED_INPUT_MAX_BYTES)
|
|
17723
|
+
throw new Error("SESSION_REFRESH_INPUT_LIMIT: hosted source and asset payload exceeds refresh byte bounds.");
|
|
17724
|
+
let ownedClaudeAuthorities;
|
|
17725
|
+
if (previous.manifest.tool === "claude") {
|
|
17726
|
+
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 }));
|
|
17727
|
+
}
|
|
17728
|
+
const station = selector.stationProfile ? stationProfileSource() : null;
|
|
17729
|
+
const plan = planProfileSessionRender({
|
|
17730
|
+
tool: previous.manifest.tool,
|
|
17731
|
+
profile: previous.manifest.profile,
|
|
17732
|
+
profile_id: profile.id,
|
|
17733
|
+
provider_version: selector.providerVersion,
|
|
17734
|
+
targetHome,
|
|
17735
|
+
...previous.manifest.targetKind === "project-root" ? { projectRoot: targetHome } : {},
|
|
17736
|
+
sessionId: previous.manifest.sessionId ?? undefined,
|
|
17737
|
+
refreshSelector: selector,
|
|
17738
|
+
codewithNativeImports: selector.codewithNativeImports,
|
|
17739
|
+
allowEmptySources: selector.allowEmptySources,
|
|
17740
|
+
configs,
|
|
17741
|
+
bindings,
|
|
17742
|
+
asset_configs: assetConfigs,
|
|
17743
|
+
asset_bindings: assetBindings,
|
|
17744
|
+
asset_plan_mode: input.dryRun ? "dry-run" : "apply",
|
|
17745
|
+
asset_scope: selector.assetScope,
|
|
17746
|
+
asset_surface: selector.assetSurface,
|
|
17747
|
+
extra_sources: station ? [station] : undefined,
|
|
17748
|
+
ownedClaudeAuthorities,
|
|
17749
|
+
graph_context: {
|
|
17750
|
+
...selector.providerVariant ? { provider_variant: selector.providerVariant } : {},
|
|
17751
|
+
...selector.model ? { model: selector.model } : {},
|
|
17752
|
+
...selector.path ? { path: selector.path } : {},
|
|
17753
|
+
...selector.manual.length ? { manual: selector.manual } : {}
|
|
17754
|
+
}
|
|
17755
|
+
});
|
|
17756
|
+
if (selector.checkGlobalCoverage) {
|
|
17757
|
+
const coverage = computeGlobalSourceCoverage(await store.listConfigs({}), accountedGlobalSourceSlugs(plan.manifest));
|
|
17758
|
+
if (!coverage.complete)
|
|
17759
|
+
throw new Error(`SESSION_REFRESH_COVERAGE_INCOMPLETE: ${coverage.missingSlugs.length} registered global sources are absent.`);
|
|
17760
|
+
}
|
|
17761
|
+
const preview = applySessionRender(plan, { dryRun: true, expectedManifestSha256: previous.sha256, ownedClaudeAuthorities });
|
|
17762
|
+
const common = { checkedAt: new Date().toISOString(), targetHome, selector, sourceHash: plan.manifest.sourceHash };
|
|
17763
|
+
if (preview.conflicts.length)
|
|
17764
|
+
return { ...common, status: "blocked", apply: preview };
|
|
17765
|
+
if (!preview.drift.clean)
|
|
17766
|
+
throw new Error("SESSION_REFRESH_DRIFT: generated files changed or disappeared since their last managed render.");
|
|
17767
|
+
const sameSources = previous.manifest.sourceHash === plan.manifest.sourceHash;
|
|
17768
|
+
const samePayloads = preview.files.filter((file) => file.role !== "manifest").every((file) => file.action === "unchanged");
|
|
17769
|
+
if (sameSources && samePayloads) {
|
|
17770
|
+
if (readManagedManifest(targetHome).sha256 !== previous.sha256 || !checkSessionRenderDrift(targetHome).clean)
|
|
17771
|
+
throw new Error("SESSION_REFRESH_DRIFT: managed target changed during refresh.");
|
|
17772
|
+
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) } };
|
|
17773
|
+
}
|
|
17774
|
+
if (input.dryRun)
|
|
17775
|
+
return { ...common, status: "dry-run", apply: preview };
|
|
17776
|
+
const applied = applySessionRender(plan, { expectedManifestSha256: previous.sha256, ownedClaudeAuthorities });
|
|
17777
|
+
if (applied.conflicts.length)
|
|
17778
|
+
return { ...common, status: "blocked", apply: applied };
|
|
17779
|
+
if (!applied.applied || !checkSessionRenderDrift(targetHome).clean)
|
|
17780
|
+
throw new Error("SESSION_REFRESH_READBACK_FAILED: applied instructions did not verify against their manifest.");
|
|
17781
|
+
return { ...common, status: "updated", apply: applied };
|
|
17782
|
+
}
|
|
17244
17783
|
|
|
17245
17784
|
// src/index.ts
|
|
17246
17785
|
init_project_context();
|
|
@@ -17248,9 +17787,11 @@ init_transforms();
|
|
|
17248
17787
|
init_machine();
|
|
17249
17788
|
|
|
17250
17789
|
// src/lib/platform-profiles.ts
|
|
17790
|
+
init_types();
|
|
17251
17791
|
init_config_store();
|
|
17252
17792
|
|
|
17253
17793
|
// src/lib/project-dashboard-standard.ts
|
|
17794
|
+
init_types();
|
|
17254
17795
|
init_config_store();
|
|
17255
17796
|
var PROJECT_DASHBOARD_STANDARD_SLUG = "agent-managed-project-dashboard-standard";
|
|
17256
17797
|
var PROJECT_DASHBOARD_PROFILE_VARIABLES = {
|
|
@@ -17343,11 +17884,15 @@ async function ensureProjectDashboardStandardConfig(store = resolveConfigStore()
|
|
|
17343
17884
|
};
|
|
17344
17885
|
try {
|
|
17345
17886
|
const existing = await store.getConfig(PROJECT_DASHBOARD_STANDARD_SLUG);
|
|
17887
|
+
if (isRetiredInstructionSource(existing))
|
|
17888
|
+
return existing;
|
|
17346
17889
|
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
17890
|
return await store.updateConfig(existing.id, input);
|
|
17348
17891
|
}
|
|
17349
17892
|
return existing;
|
|
17350
|
-
} catch {
|
|
17893
|
+
} catch (error) {
|
|
17894
|
+
if (!(error instanceof ConfigNotFoundError))
|
|
17895
|
+
throw error;
|
|
17351
17896
|
return await store.createConfig(input);
|
|
17352
17897
|
}
|
|
17353
17898
|
}
|
|
@@ -17360,10 +17905,9 @@ function profileHasSelectors2(profile) {
|
|
|
17360
17905
|
var PLATFORM_PROFILE_PRESETS = [
|
|
17361
17906
|
{
|
|
17362
17907
|
name: "linux-arm64",
|
|
17363
|
-
description: "Default
|
|
17364
|
-
selectors: { os: ["linux"], arch: ["arm64"]
|
|
17908
|
+
description: "Default reviewed instruction profile for Linux arm64",
|
|
17909
|
+
selectors: { os: ["linux"], arch: ["arm64"] },
|
|
17365
17910
|
variables: {
|
|
17366
|
-
WORKSPACE_ROOT: "{{HOME_DIR}}/workspace",
|
|
17367
17911
|
BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
|
|
17368
17912
|
BUN_PATH: "{{BUN_BIN_DIR}}/bun",
|
|
17369
17913
|
PATH_PREFIX: "{{BUN_BIN_DIR}}",
|
|
@@ -17372,10 +17916,9 @@ var PLATFORM_PROFILE_PRESETS = [
|
|
|
17372
17916
|
},
|
|
17373
17917
|
{
|
|
17374
17918
|
name: "macos-arm64",
|
|
17375
|
-
description: "Default
|
|
17376
|
-
selectors: { os: ["macos"], arch: ["arm64"]
|
|
17919
|
+
description: "Default reviewed instruction profile for macOS arm64",
|
|
17920
|
+
selectors: { os: ["macos"], arch: ["arm64"] },
|
|
17377
17921
|
variables: {
|
|
17378
|
-
WORKSPACE_ROOT: "{{HOME_DIR}}/Workspace",
|
|
17379
17922
|
BUN_BIN_DIR: "{{HOME_DIR}}/.bun/bin",
|
|
17380
17923
|
BUN_PATH: "/opt/homebrew/bin/bun",
|
|
17381
17924
|
PATH_PREFIX: "/opt/homebrew/bin:{{BUN_BIN_DIR}}",
|
|
@@ -17384,12 +17927,21 @@ var PLATFORM_PROFILE_PRESETS = [
|
|
|
17384
17927
|
}
|
|
17385
17928
|
];
|
|
17386
17929
|
async function ensurePlatformProfiles(store = resolveConfigStore()) {
|
|
17387
|
-
const configs = await store.listConfigs();
|
|
17388
17930
|
const ensured = [];
|
|
17389
|
-
|
|
17390
|
-
let profile;
|
|
17931
|
+
const existingProfiles = await Promise.all(PLATFORM_PROFILE_PRESETS.map(async (preset) => {
|
|
17391
17932
|
try {
|
|
17392
|
-
|
|
17933
|
+
return await store.getProfile(preset.name);
|
|
17934
|
+
} catch (error) {
|
|
17935
|
+
if (error instanceof ProfileNotFoundError)
|
|
17936
|
+
return null;
|
|
17937
|
+
throw error;
|
|
17938
|
+
}
|
|
17939
|
+
}));
|
|
17940
|
+
for (const [index, preset] of PLATFORM_PROFILE_PRESETS.entries()) {
|
|
17941
|
+
let profile;
|
|
17942
|
+
const existing = existingProfiles[index];
|
|
17943
|
+
if (existing) {
|
|
17944
|
+
profile = existing;
|
|
17393
17945
|
const selectors = mergeProfileSelectors(preset.selectors, profile.selectors);
|
|
17394
17946
|
const variables = mergeProfileVariables(preset.variables, profile.variables);
|
|
17395
17947
|
if (JSON.stringify(selectors) !== JSON.stringify(profile.selectors) || JSON.stringify(variables) !== JSON.stringify(profile.variables)) {
|
|
@@ -17399,12 +17951,9 @@ async function ensurePlatformProfiles(store = resolveConfigStore()) {
|
|
|
17399
17951
|
variables
|
|
17400
17952
|
});
|
|
17401
17953
|
}
|
|
17402
|
-
}
|
|
17954
|
+
} else {
|
|
17403
17955
|
profile = await store.createProfile(preset);
|
|
17404
17956
|
}
|
|
17405
|
-
for (const config of configs) {
|
|
17406
|
-
await store.addConfigToProfile(profile.id, config.id);
|
|
17407
|
-
}
|
|
17408
17957
|
ensured.push(profile);
|
|
17409
17958
|
}
|
|
17410
17959
|
return ensured;
|
|
@@ -17438,6 +17987,7 @@ function mergeUnique(preset, existing) {
|
|
|
17438
17987
|
init_global_agent_rules_standard();
|
|
17439
17988
|
|
|
17440
17989
|
// src/lib/dangerous-operation-guard-standard.ts
|
|
17990
|
+
init_types();
|
|
17441
17991
|
init_config_store();
|
|
17442
17992
|
var DANGEROUS_OPERATION_GUARD_STANDARD_SLUG = "dangerous-operation-guard-standard";
|
|
17443
17993
|
var DANGEROUS_OPERATION_GUARD_STANDARD_CONTENT = `# Dangerous Operation Guard Standard
|
|
@@ -17549,11 +18099,15 @@ async function ensureDangerousOperationGuardStandardConfig(store = resolveConfig
|
|
|
17549
18099
|
};
|
|
17550
18100
|
try {
|
|
17551
18101
|
const existing = await store.getConfig(DANGEROUS_OPERATION_GUARD_STANDARD_SLUG);
|
|
18102
|
+
if (isRetiredInstructionSource(existing))
|
|
18103
|
+
return existing;
|
|
17552
18104
|
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
18105
|
return await store.updateConfig(existing.id, input);
|
|
17554
18106
|
}
|
|
17555
18107
|
return existing;
|
|
17556
|
-
} catch {
|
|
18108
|
+
} catch (error) {
|
|
18109
|
+
if (!(error instanceof ConfigNotFoundError))
|
|
18110
|
+
throw error;
|
|
17557
18111
|
return await store.createConfig(input);
|
|
17558
18112
|
}
|
|
17559
18113
|
}
|
|
@@ -17563,8 +18117,8 @@ init_codewith_shared_todos_storage_standard();
|
|
|
17563
18117
|
|
|
17564
18118
|
// src/lib/sync.ts
|
|
17565
18119
|
init_config_store();
|
|
17566
|
-
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as
|
|
17567
|
-
import { basename as
|
|
18120
|
+
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync17 } from "fs";
|
|
18121
|
+
import { basename as basename7, extname as extname3, join as join20 } from "path";
|
|
17568
18122
|
init_config_agents();
|
|
17569
18123
|
init_redact();
|
|
17570
18124
|
init_machine();
|
|
@@ -17572,8 +18126,8 @@ init_transforms();
|
|
|
17572
18126
|
|
|
17573
18127
|
// src/lib/sync-dir.ts
|
|
17574
18128
|
init_config_store();
|
|
17575
|
-
import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as
|
|
17576
|
-
import { join as
|
|
18129
|
+
import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync16, statSync as statSync6 } from "fs";
|
|
18130
|
+
import { join as join19, relative as relative7 } from "path";
|
|
17577
18131
|
init_redact();
|
|
17578
18132
|
var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
|
|
17579
18133
|
function shouldSkip(p) {
|
|
@@ -17584,7 +18138,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
17584
18138
|
const absDir = expandPath(dir);
|
|
17585
18139
|
if (!existsSync16(absDir))
|
|
17586
18140
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
17587
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync4(absDir).map((f) =>
|
|
18141
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync4(absDir).map((f) => join19(absDir, f)).filter((f) => statSync6(f).isFile());
|
|
17588
18142
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
17589
18143
|
const allConfigs = await store.listConfigs();
|
|
17590
18144
|
for (const file of files) {
|
|
@@ -17593,7 +18147,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
17593
18147
|
continue;
|
|
17594
18148
|
}
|
|
17595
18149
|
try {
|
|
17596
|
-
const content =
|
|
18150
|
+
const content = readFileSync16(file, "utf-8");
|
|
17597
18151
|
if (content.length > 500000) {
|
|
17598
18152
|
result.skipped.push(file + " (too large)");
|
|
17599
18153
|
continue;
|
|
@@ -17643,7 +18197,7 @@ async function syncToDir(dir, opts = {}) {
|
|
|
17643
18197
|
}
|
|
17644
18198
|
function walkDir(dir, files = []) {
|
|
17645
18199
|
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
17646
|
-
const full =
|
|
18200
|
+
const full = join19(dir, entry.name);
|
|
17647
18201
|
if (shouldSkip(full))
|
|
17648
18202
|
continue;
|
|
17649
18203
|
if (entry.isDirectory())
|
|
@@ -17665,7 +18219,7 @@ var CLAUDE_PROMPT_OUTPUTS = [
|
|
|
17665
18219
|
{ agent: "qwen", target_path: "~/.qwen/QWEN.md", transform: "codex-flat" }
|
|
17666
18220
|
];
|
|
17667
18221
|
function claudeRuleOutputs(fileName) {
|
|
17668
|
-
const stem =
|
|
18222
|
+
const stem = basename7(fileName, extname3(fileName));
|
|
17669
18223
|
return [
|
|
17670
18224
|
{ agent: "cursor", target_path: `~/.cursor/rules/${stem}.mdc`, transform: "cursor-mdc" }
|
|
17671
18225
|
];
|
|
@@ -17711,7 +18265,7 @@ function hasClaudeRuleSourceForCursorTarget(targetPath) {
|
|
|
17711
18265
|
const absolutePrefix = expandPath("~/.cursor/rules");
|
|
17712
18266
|
if (!absoluteTargetPath.startsWith(`${absolutePrefix}/`) || !absoluteTargetPath.endsWith(".mdc"))
|
|
17713
18267
|
return false;
|
|
17714
|
-
const stem =
|
|
18268
|
+
const stem = basename7(absoluteTargetPath, ".mdc");
|
|
17715
18269
|
return existsSync17(expandPath(`~/.claude/rules/${stem}.md`)) || existsSync17(expandPath(`~/.claude/rules/${stem}.mdc`));
|
|
17716
18270
|
}
|
|
17717
18271
|
function isKnownGeneratedTargetPath(targetPath) {
|
|
@@ -17777,11 +18331,11 @@ async function syncProject(opts) {
|
|
|
17777
18331
|
const allConfigs = await store.listConfigs();
|
|
17778
18332
|
const machine = detectMachineContext();
|
|
17779
18333
|
for (const pf of PROJECT_CONFIG_FILES) {
|
|
17780
|
-
const abs =
|
|
18334
|
+
const abs = join20(absDir, pf.file);
|
|
17781
18335
|
if (!existsSync17(abs))
|
|
17782
18336
|
continue;
|
|
17783
18337
|
try {
|
|
17784
|
-
const rawContent =
|
|
18338
|
+
const rawContent = readFileSync17(abs, "utf-8");
|
|
17785
18339
|
if (rawContent.length > 500000) {
|
|
17786
18340
|
result.skipped.push(pf.file);
|
|
17787
18341
|
continue;
|
|
@@ -17810,20 +18364,20 @@ async function syncProject(opts) {
|
|
|
17810
18364
|
}
|
|
17811
18365
|
}
|
|
17812
18366
|
for (const ruleDir of [
|
|
17813
|
-
{ dir:
|
|
17814
|
-
{ dir:
|
|
17815
|
-
{ dir:
|
|
17816
|
-
{ dir:
|
|
17817
|
-
{ dir:
|
|
17818
|
-
{ dir:
|
|
17819
|
-
{ dir:
|
|
18367
|
+
{ dir: join20(absDir, ".claude", "rules"), agent: "claude", namePrefix: "rules" },
|
|
18368
|
+
{ dir: join20(absDir, ".agents", "rules"), agent: "antigravity", namePrefix: "antigravity-rules" },
|
|
18369
|
+
{ dir: join20(absDir, ".cursor", "rules"), agent: "cursor", namePrefix: "cursor-rules" },
|
|
18370
|
+
{ dir: join20(absDir, ".github", "instructions"), agent: "copilot", namePrefix: "copilot-instructions" },
|
|
18371
|
+
{ dir: join20(absDir, ".devin", "rules"), agent: "devin", namePrefix: "devin-rules" },
|
|
18372
|
+
{ dir: join20(absDir, ".windsurf", "rules"), agent: "windsurf-legacy", namePrefix: "windsurf-rules" },
|
|
18373
|
+
{ dir: join20(absDir, ".clinerules"), agent: "cline", namePrefix: "cline-rules" }
|
|
17820
18374
|
]) {
|
|
17821
18375
|
if (!existsSync17(ruleDir.dir))
|
|
17822
18376
|
continue;
|
|
17823
18377
|
const mdFiles = readdirSync5(ruleDir.dir).filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
17824
18378
|
for (const f of mdFiles) {
|
|
17825
|
-
const abs =
|
|
17826
|
-
const raw =
|
|
18379
|
+
const abs = join20(ruleDir.dir, f);
|
|
18380
|
+
const raw = readFileSync17(abs, "utf-8");
|
|
17827
18381
|
const redacted = redactContent(raw, "markdown");
|
|
17828
18382
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
17829
18383
|
const content = machineAware.content;
|
|
@@ -17869,13 +18423,13 @@ async function syncKnown(opts = {}) {
|
|
|
17869
18423
|
const extensions = known.rulesExtensions ?? [".md", ".mdc"];
|
|
17870
18424
|
const ruleFiles = readdirSync5(absDir).filter((f) => extensions.some((ext) => f.endsWith(ext)));
|
|
17871
18425
|
for (const f of ruleFiles) {
|
|
17872
|
-
const abs2 =
|
|
18426
|
+
const abs2 = join20(absDir, f);
|
|
17873
18427
|
const targetPath = abs2.replace(home, "~");
|
|
17874
18428
|
if (existingOutputOwners.has(normalizeTargetPath(targetPath)) || isKnownGeneratedTargetPath(targetPath)) {
|
|
17875
18429
|
result.skipped.push(`${targetPath} (generated output)`);
|
|
17876
18430
|
continue;
|
|
17877
18431
|
}
|
|
17878
|
-
const raw =
|
|
18432
|
+
const raw = readFileSync17(abs2, "utf-8");
|
|
17879
18433
|
const redacted = redactContent(raw, "markdown");
|
|
17880
18434
|
const machineAware = templateizeMachineContent(redacted.content, machine);
|
|
17881
18435
|
const content = machineAware.content;
|
|
@@ -17908,7 +18462,7 @@ async function syncKnown(opts = {}) {
|
|
|
17908
18462
|
continue;
|
|
17909
18463
|
}
|
|
17910
18464
|
try {
|
|
17911
|
-
const rawContent = normalizeKnownConfigSource(known,
|
|
18465
|
+
const rawContent = normalizeKnownConfigSource(known, readFileSync17(abs, "utf-8"));
|
|
17912
18466
|
if (rawContent.length > 500000) {
|
|
17913
18467
|
result.skipped.push(known.path + " (too large)");
|
|
17914
18468
|
continue;
|
|
@@ -18013,7 +18567,7 @@ function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
|
18013
18567
|
const path = expandPath(targetPath);
|
|
18014
18568
|
if (!existsSync17(path))
|
|
18015
18569
|
return `(file not found on disk: ${path})`;
|
|
18016
|
-
const diskContent =
|
|
18570
|
+
const diskContent = readFileSync17(path, "utf-8");
|
|
18017
18571
|
if (diskContent === expectedContent)
|
|
18018
18572
|
return "(no diff \u2014 identical)";
|
|
18019
18573
|
const format = redactFormatForTarget(targetPath, storedFormat);
|
|
@@ -18173,9 +18727,9 @@ function detectFormat(filePath) {
|
|
|
18173
18727
|
// src/lib/export.ts
|
|
18174
18728
|
init_types();
|
|
18175
18729
|
init_config_store();
|
|
18176
|
-
import { createHash as
|
|
18730
|
+
import { createHash as createHash12 } from "crypto";
|
|
18177
18731
|
import { existsSync as existsSync18, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
18178
|
-
import { dirname as
|
|
18732
|
+
import { dirname as dirname12, join as join21, resolve as resolve13 } from "path";
|
|
18179
18733
|
import { tmpdir } from "os";
|
|
18180
18734
|
var ARCHIVE_EXCLUSIONS = [
|
|
18181
18735
|
{
|
|
@@ -18198,7 +18752,7 @@ function compareText(left, right) {
|
|
|
18198
18752
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
18199
18753
|
}
|
|
18200
18754
|
function sha25611(value) {
|
|
18201
|
-
return
|
|
18755
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
18202
18756
|
}
|
|
18203
18757
|
function canonicalize(value) {
|
|
18204
18758
|
if (Array.isArray(value))
|
|
@@ -18511,13 +19065,13 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
18511
19065
|
integrity,
|
|
18512
19066
|
exclusions: ARCHIVE_EXCLUSIONS
|
|
18513
19067
|
};
|
|
18514
|
-
const absOutput =
|
|
18515
|
-
mkdirSync8(
|
|
18516
|
-
const stagingDir = mkdtempSync(
|
|
19068
|
+
const absOutput = resolve13(outputPath);
|
|
19069
|
+
mkdirSync8(dirname12(absOutput), { recursive: true });
|
|
19070
|
+
const stagingDir = mkdtempSync(join21(tmpdir(), "instructions-domain-export-"));
|
|
18517
19071
|
try {
|
|
18518
|
-
writeFileSync6(
|
|
19072
|
+
writeFileSync6(join21(stagingDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
18519
19073
|
`, "utf-8");
|
|
18520
|
-
writeFileSync6(
|
|
19074
|
+
writeFileSync6(join21(stagingDir, "domain.json"), payload, "utf-8");
|
|
18521
19075
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", stagingDir, "manifest.json", "domain.json"], {
|
|
18522
19076
|
stdout: "pipe",
|
|
18523
19077
|
stderr: "pipe"
|
|
@@ -18536,8 +19090,8 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
18536
19090
|
// src/lib/import.ts
|
|
18537
19091
|
init_types();
|
|
18538
19092
|
init_config_store();
|
|
18539
|
-
import { createHash as
|
|
18540
|
-
import { resolve as
|
|
19093
|
+
import { createHash as createHash13 } from "crypto";
|
|
19094
|
+
import { resolve as resolve14 } from "path";
|
|
18541
19095
|
function compareText2(left, right) {
|
|
18542
19096
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
18543
19097
|
}
|
|
@@ -18562,7 +19116,7 @@ function emptyResult() {
|
|
|
18562
19116
|
};
|
|
18563
19117
|
}
|
|
18564
19118
|
function sha25612(value) {
|
|
18565
|
-
return
|
|
19119
|
+
return createHash13("sha256").update(value).digest("hex");
|
|
18566
19120
|
}
|
|
18567
19121
|
function normalizeMemberName(name) {
|
|
18568
19122
|
let normalized = name;
|
|
@@ -18825,7 +19379,7 @@ async function importV1(reader, manifest, store, conflict) {
|
|
|
18825
19379
|
async function importConfigs(bundlePath, opts = {}) {
|
|
18826
19380
|
const store = opts.store ?? resolveConfigStore();
|
|
18827
19381
|
const conflict = opts.conflict ?? "skip";
|
|
18828
|
-
const reader = await openArchive(
|
|
19382
|
+
const reader = await openArchive(resolve14(bundlePath));
|
|
18829
19383
|
const manifestText = await reader.read("manifest.json");
|
|
18830
19384
|
let manifest;
|
|
18831
19385
|
try {
|
|
@@ -18853,9 +19407,9 @@ init_redact();
|
|
|
18853
19407
|
|
|
18854
19408
|
// src/lib/package-manager-guard.ts
|
|
18855
19409
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
18856
|
-
import { existsSync as existsSync19, lstatSync as
|
|
19410
|
+
import { existsSync as existsSync19, lstatSync as lstatSync8, readdirSync as readdirSync6, readFileSync as readFileSync18 } from "fs";
|
|
18857
19411
|
import { homedir as homedir10 } from "os";
|
|
18858
|
-
import { basename as
|
|
19412
|
+
import { basename as basename8, dirname as dirname13, isAbsolute as isAbsolute8, join as join22, relative as relative8, resolve as resolve15 } from "path";
|
|
18859
19413
|
var SKIP_DIRS = new Set([
|
|
18860
19414
|
".git",
|
|
18861
19415
|
"node_modules",
|
|
@@ -18892,14 +19446,14 @@ var TOKEN_VALUE_PATTERNS = [
|
|
|
18892
19446
|
{ re: /xoxb-[0-9]+-[A-Za-z0-9-]+/, rule: "literal-slack-token", detail: "literal Slack token-like value" }
|
|
18893
19447
|
];
|
|
18894
19448
|
function scanPackageManagerSecrets(options = {}) {
|
|
18895
|
-
const cwd = options.cwd ?
|
|
18896
|
-
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) =>
|
|
19449
|
+
const cwd = options.cwd ? resolve15(options.cwd) : process.cwd();
|
|
19450
|
+
const roots = (options.roots && options.roots.length > 0 ? options.roots : [cwd]).map((root) => resolve15(cwd, root));
|
|
18897
19451
|
const findings = [];
|
|
18898
19452
|
let scannedFiles = 0;
|
|
18899
19453
|
for (const root of roots) {
|
|
18900
19454
|
if (!existsSync19(root))
|
|
18901
19455
|
continue;
|
|
18902
|
-
const stat =
|
|
19456
|
+
const stat = lstatSync8(root);
|
|
18903
19457
|
if (stat.isFile()) {
|
|
18904
19458
|
if (!shouldScanRepoFile(root))
|
|
18905
19459
|
continue;
|
|
@@ -18907,7 +19461,7 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
18907
19461
|
if (text === null)
|
|
18908
19462
|
continue;
|
|
18909
19463
|
scannedFiles++;
|
|
18910
|
-
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root),
|
|
19464
|
+
findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname13(root)));
|
|
18911
19465
|
continue;
|
|
18912
19466
|
}
|
|
18913
19467
|
if (!stat.isDirectory())
|
|
@@ -18926,7 +19480,7 @@ function scanPackageManagerSecrets(options = {}) {
|
|
|
18926
19480
|
if (options.includeHome) {
|
|
18927
19481
|
const home = homedir10();
|
|
18928
19482
|
for (const name of HOME_FILES) {
|
|
18929
|
-
const file =
|
|
19483
|
+
const file = join22(home, name);
|
|
18930
19484
|
if (!existsSync19(file))
|
|
18931
19485
|
continue;
|
|
18932
19486
|
const text = readTextFile(file);
|
|
@@ -18951,12 +19505,12 @@ function collectRepoFiles(root) {
|
|
|
18951
19505
|
if (entry.isDirectory()) {
|
|
18952
19506
|
if (SKIP_DIRS.has(entry.name))
|
|
18953
19507
|
continue;
|
|
18954
|
-
visit(
|
|
19508
|
+
visit(join22(dir, entry.name));
|
|
18955
19509
|
continue;
|
|
18956
19510
|
}
|
|
18957
19511
|
if (!entry.isFile())
|
|
18958
19512
|
continue;
|
|
18959
|
-
const file =
|
|
19513
|
+
const file = join22(dir, entry.name);
|
|
18960
19514
|
if (shouldScanRepoFile(file))
|
|
18961
19515
|
out.push(file);
|
|
18962
19516
|
}
|
|
@@ -18965,11 +19519,11 @@ function collectRepoFiles(root) {
|
|
|
18965
19519
|
return out;
|
|
18966
19520
|
}
|
|
18967
19521
|
function shouldScanRepoFile(file) {
|
|
18968
|
-
const name =
|
|
19522
|
+
const name = basename8(file);
|
|
18969
19523
|
return isNpmrcName(name) || isBunConfigName(name) || LOCKFILE_NAMES.has(name);
|
|
18970
19524
|
}
|
|
18971
19525
|
function classifyRepoFile(file) {
|
|
18972
|
-
const name =
|
|
19526
|
+
const name = basename8(file);
|
|
18973
19527
|
if (isNpmrcName(name))
|
|
18974
19528
|
return "repo-npmrc";
|
|
18975
19529
|
if (isBunConfigName(name))
|
|
@@ -18991,10 +19545,10 @@ function isNpmrcName(name) {
|
|
|
18991
19545
|
}
|
|
18992
19546
|
function readTextFile(file) {
|
|
18993
19547
|
try {
|
|
18994
|
-
const stat =
|
|
19548
|
+
const stat = lstatSync8(file);
|
|
18995
19549
|
if (!stat.isFile() || stat.size > 5000000)
|
|
18996
19550
|
return null;
|
|
18997
|
-
const buf =
|
|
19551
|
+
const buf = readFileSync18(file);
|
|
18998
19552
|
if (buf.includes(0))
|
|
18999
19553
|
return null;
|
|
19000
19554
|
return buf.toString("utf-8");
|
|
@@ -19194,7 +19748,7 @@ function trackedFiles(root) {
|
|
|
19194
19748
|
}
|
|
19195
19749
|
function isTrackedFile(file) {
|
|
19196
19750
|
try {
|
|
19197
|
-
const repoRoot = execFileSync2("git", ["-C",
|
|
19751
|
+
const repoRoot = execFileSync2("git", ["-C", dirname13(file), "rev-parse", "--show-toplevel"], {
|
|
19198
19752
|
encoding: "utf-8",
|
|
19199
19753
|
stdio: ["ignore", "pipe", "ignore"]
|
|
19200
19754
|
}).trim();
|
|
@@ -19237,6 +19791,7 @@ function toPosix(path) {
|
|
|
19237
19791
|
return path.split("\\").join("/");
|
|
19238
19792
|
}
|
|
19239
19793
|
export {
|
|
19794
|
+
validateExpectedConfigVersion,
|
|
19240
19795
|
uuid,
|
|
19241
19796
|
transformSkillContent,
|
|
19242
19797
|
templateizeMachineContent,
|
|
@@ -19272,6 +19827,7 @@ export {
|
|
|
19272
19827
|
renderMachineAwareContentPreview,
|
|
19273
19828
|
renderMachineAwareContent,
|
|
19274
19829
|
refreshStationProfile,
|
|
19830
|
+
refreshSessionRender,
|
|
19275
19831
|
redactContent,
|
|
19276
19832
|
reconcileManagedSkillRuntimes,
|
|
19277
19833
|
readStationProfile,
|
|
@@ -19285,6 +19841,7 @@ export {
|
|
|
19285
19841
|
parseProjectContextBundle,
|
|
19286
19842
|
parseAgentOperatingRulesVersion,
|
|
19287
19843
|
now,
|
|
19844
|
+
normalizeSessionHostedProfileSelector,
|
|
19288
19845
|
normalizeProfileConfigBinding,
|
|
19289
19846
|
normalizeProfileAssetBinding,
|
|
19290
19847
|
normalizeOsFamily,
|
|
@@ -19331,6 +19888,7 @@ export {
|
|
|
19331
19888
|
compareAgentOperatingRulesVersions,
|
|
19332
19889
|
cleanSessionPathInput,
|
|
19333
19890
|
checkSessionRenderDrift,
|
|
19891
|
+
buildSumiAgentsMd,
|
|
19334
19892
|
buildStationProfileBlock,
|
|
19335
19893
|
buildOpenCodeAgentsMd,
|
|
19336
19894
|
buildCursorMdc,
|
|
@@ -19390,6 +19948,7 @@ export {
|
|
|
19390
19948
|
LEGACY_CONFIGS_EXECUTABLE,
|
|
19391
19949
|
LEGACY_CONFIGS_COMPAT_VERSION,
|
|
19392
19950
|
KNOWN_CONFIGS,
|
|
19951
|
+
InvalidExpectedVersionError,
|
|
19393
19952
|
InstructionGraphValidationError,
|
|
19394
19953
|
INSTRUCTION_GRAPH_PLAN_SCHEMA,
|
|
19395
19954
|
INSTRUCTION_FALLBACKS,
|
|
@@ -19402,6 +19961,7 @@ export {
|
|
|
19402
19961
|
GLOBAL_AGENT_RULES_STANDARD_CONTENT,
|
|
19403
19962
|
DANGEROUS_OPERATION_GUARD_STANDARD_SLUG,
|
|
19404
19963
|
DANGEROUS_OPERATION_GUARD_STANDARD_CONTENT,
|
|
19964
|
+
ConfigVersionConflictError,
|
|
19405
19965
|
ConfigNotFoundError,
|
|
19406
19966
|
ConfigApplyError,
|
|
19407
19967
|
CloudConfigStore,
|