@inneranimalmedia/agentsam-sdk 2.4.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/AGENTSAM.md +55 -0
  2. package/README.md +17 -12
  3. package/bin/agentsam +2 -0
  4. package/docs/AGENTSAM_ASTRA_OPENAI_INTEGRATION.md +1363 -0
  5. package/docs/AGENTSAM_WORKBENCH.md +30 -0
  6. package/docs/AUTH_IDENTITY_CONTRACT.md +52 -0
  7. package/docs/CAPABILITIES.md +5 -3
  8. package/docs/CLI_SHELL.md +167 -80
  9. package/docs/CMS_STUDIO.md +101 -0
  10. package/docs/CONTEXT.md +170 -0
  11. package/docs/LOCAL_OLLAMA.md +58 -0
  12. package/docs/MERKLE.md +19 -19
  13. package/docs/PORTABLE_CONTEXT.md +4 -3
  14. package/docs/PROJECT_CONFIG.md +72 -0
  15. package/docs/RELEASES.md +35 -2
  16. package/docs/REPOSITORY_INTELLIGENCE.md +1 -1
  17. package/docs/REPOSITORY_KNOWLEDGE.md +114 -0
  18. package/docs/SDK_WORKER.md +86 -0
  19. package/docs/SECURITY.md +59 -22
  20. package/docs/client-cms-editor.md +15 -0
  21. package/docs/local-studio/WORKMODE_DONOR_NOTES.md +485 -0
  22. package/package.json +35 -10
  23. package/packages/connectors/cloudflare/package.json +10 -0
  24. package/packages/connectors/cloudflare/src/index.js +127 -0
  25. package/packages/connectors/cloudflare/src/owner.js +76 -0
  26. package/packages/connectors/cloudflare/src/routes.js +223 -0
  27. package/packages/connectors/cloudflare/src/vault.js +80 -0
  28. package/packages/connectors/cloudflare/tests/connector.test.mjs +44 -0
  29. package/packages/identity/package.json +10 -1
  30. package/packages/identity/src/contracts/auth-config.js +109 -0
  31. package/packages/identity/src/index.js +1 -0
  32. package/packages/identity/src/oauth/README.md +2 -2
  33. package/packages/identity/src/oauth/credentials.js +11 -4
  34. package/packages/identity/src/oauth/iam-platform.js +3 -3
  35. package/packages/identity/src/providers/iam/index.js +7 -7
  36. package/packages/identity/src/providers/iam/oauth.js +6 -4
  37. package/packages/identity/src/providers/iam/profile.js +5 -5
  38. package/packages/identity/tests/auth-config.test.mjs +61 -0
  39. package/packages/identity/tests/oauth-credentials.test.mjs +13 -2
  40. package/protocol/FILEMETA_V1.md +95 -0
  41. package/protocol/INSPECT_VIEWS_V1.md +40 -0
  42. package/protocol/MERKLE_PERSISTENCE_V1.md +50 -0
  43. package/protocol/MERKLE_V1.md +3 -1
  44. package/protocol/README.md +1 -0
  45. package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +19 -0
  46. package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +13 -0
  47. package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +19 -0
  48. package/protocol/capabilities/manifest.json +79 -3
  49. package/protocol/capabilities/repository-snapshot.schema.json +1 -0
  50. package/protocol/context/context-budget.schema.json +36 -0
  51. package/protocol/context/context-item.schema.json +19 -0
  52. package/protocol/context/resolved-context-pack.schema.json +47 -0
  53. package/protocol/context/result-policy.schema.json +17 -0
  54. package/protocol/knowledge/context-pack.schema.json +33 -0
  55. package/protocol/knowledge/retrieval-query.schema.json +68 -13
  56. package/protocol/models/README.md +373 -0
  57. package/protocol/models/model-inventory-v2.schema.json +212 -0
  58. package/python/README.md +15 -7
  59. package/python/agentsam_sdk/cli.py +0 -21
  60. package/python/agentsam_sdk/tui/README.md +17 -12
  61. package/python/agentsam_sdk/tui/bootstrap.py +2 -2
  62. package/python/agentsam_sdk/tui/demo.py +25 -10
  63. package/python/agentsam_sdk/tui/onboarding.py +208 -0
  64. package/python/tests/test_tui_cli.py +7 -6
  65. package/skills/README.md +21 -0
  66. package/skills/agentsam-app-fundamentals/SKILL.md +165 -0
  67. package/skills/agentsam-app-fundamentals/references/graphs-contracts-ast-merkle.md +89 -0
  68. package/skills/agentsam-app-fundamentals/references/trust-credentials-and-destinations.md +99 -0
  69. package/skills/agentsam-cloudflare-workers/SKILL.md +53 -0
  70. package/skills/agentsam-cloudflare-workers/references/cpu-profiling.md +16 -0
  71. package/skills/agentsam-cloudflare-workers/references/errors-and-observability.md +29 -0
  72. package/skills/agentsam-cloudflare-workers/references/wrangler-native-map.md +28 -0
  73. package/skills/agentsam-jr-dev/SKILL.md +232 -0
  74. package/skills/agentsam-jr-dev/references/real-application-logic.md +156 -0
  75. package/skills/agentsam-jr-dev/references/web-application-fundamentals.md +240 -0
  76. package/skills/agentsam-progression-guard/SKILL.md +197 -0
  77. package/skills/agentsam-progression-guard/references/checkpoint-chain.md +111 -0
  78. package/skills/agentsam-progression-guard/references/hooks-operational-io.md +96 -0
  79. package/skills/catalog.json +71 -0
  80. package/src/agent/capability-adapter.js +25 -13
  81. package/src/agent/index.js +1 -0
  82. package/src/agent/responses-runner.js +325 -0
  83. package/src/capabilities/index.js +7 -0
  84. package/src/capabilities/repository-snapshot-view.js +238 -0
  85. package/src/capabilities/repository-snapshot.js +28 -14
  86. package/src/cli.js +198 -76
  87. package/src/cloudflare/cpu-profile.js +115 -0
  88. package/src/cloudflare/index.js +14 -0
  89. package/src/cloudflare/wrangler.js +132 -0
  90. package/src/commands/account-auth.js +47 -0
  91. package/src/commands/cloudflare.js +58 -0
  92. package/src/commands/connections.js +93 -0
  93. package/src/commands/context-economics.js +114 -0
  94. package/src/commands/context.js +14 -2
  95. package/src/commands/db.js +4 -7
  96. package/src/commands/deploy.js +54 -32
  97. package/src/commands/eval.js +63 -0
  98. package/src/commands/interactive.js +18 -0
  99. package/src/commands/knowledge.js +27 -4
  100. package/src/commands/merkle-persist.js +118 -0
  101. package/src/commands/merkle.js +32 -17
  102. package/src/commands/models.js +152 -0
  103. package/src/commands/ollama.js +259 -0
  104. package/src/commands/preferences.js +144 -0
  105. package/src/commands/product.js +86 -16
  106. package/src/commands/resume.js +67 -0
  107. package/src/commands/security.js +8 -6
  108. package/src/commands/shell.js +405 -100
  109. package/src/commands/skills.js +66 -0
  110. package/src/commands/start-local.js +1 -1
  111. package/src/commands/tunnel.js +5 -4
  112. package/src/commands/whoami.js +86 -0
  113. package/src/context/budget.js +120 -0
  114. package/src/context/compact.js +28 -0
  115. package/src/context/index.js +7 -0
  116. package/src/context/rehydrate.js +35 -0
  117. package/src/context/resolve.js +116 -0
  118. package/src/context/result-policy.js +66 -0
  119. package/src/errors/diagnostic.js +160 -0
  120. package/src/errors/index.js +9 -0
  121. package/src/eval/context.js +191 -0
  122. package/src/eval/index.js +1 -0
  123. package/src/index.js +70 -0
  124. package/src/indexing/execution-boundary.js +144 -0
  125. package/src/indexing/index.js +8 -0
  126. package/src/indexing/provider.js +41 -0
  127. package/src/knowledge/config.js +2 -2
  128. package/src/knowledge/context-pack.js +12 -1
  129. package/src/knowledge/contracts.js +9 -4
  130. package/src/knowledge/engine.js +7 -3
  131. package/src/knowledge/service/server.js +1 -1
  132. package/src/lib/account-session.js +98 -0
  133. package/src/lib/agent-instructions.js +73 -0
  134. package/src/lib/auth.js +4 -0
  135. package/src/lib/bridge-client.js +7 -5
  136. package/src/lib/cli-preferences.js +78 -0
  137. package/src/lib/core-client.js +8 -8
  138. package/src/lib/deploy/git-guard.js +69 -0
  139. package/src/lib/deploy/health.js +57 -0
  140. package/src/lib/deploy/local-studio.js +283 -0
  141. package/src/lib/deploy/secret-scan.js +65 -0
  142. package/src/lib/deploy-receipt/index.js +5 -2
  143. package/src/lib/detect-context.js +6 -5
  144. package/src/lib/execution-approvals.js +59 -0
  145. package/src/lib/identity-scaffold.js +1 -1
  146. package/src/lib/local-scaffold.js +35 -33
  147. package/src/lib/local-sessions.js +127 -0
  148. package/src/lib/local-status.js +9 -17
  149. package/src/lib/merkle/cloudflare-persistence.js +321 -0
  150. package/src/lib/merkle/filemeta.js +43 -0
  151. package/src/lib/merkle/git-ignore.js +24 -0
  152. package/src/lib/merkle/hash.js +1 -0
  153. package/src/lib/merkle/index.js +22 -0
  154. package/src/lib/merkle/persistence.js +72 -0
  155. package/src/lib/merkle/semantic.js +359 -0
  156. package/src/lib/merkle/snapshot.js +9 -3
  157. package/src/lib/merkle/tree.js +11 -6
  158. package/src/lib/project-config.js +227 -0
  159. package/src/lib/project-rules.js +68 -0
  160. package/src/lib/provider-credentials.js +83 -0
  161. package/src/lib/save-sdk-token.js +1 -1
  162. package/src/lib/scaffold/templates/worker-api/index.js +101 -20
  163. package/src/lib/scaffold/wizards/worker-api.js +27 -11
  164. package/src/lib/slash-commands.js +22 -15
  165. package/src/lib/tools.js +11 -5
  166. package/src/models/catalog.js +135 -0
  167. package/src/models/index.js +7 -0
  168. package/src/providers/index.js +5 -0
  169. package/src/providers/openai-responses.js +275 -0
  170. package/src/security/index.js +1 -0
  171. package/src/security/inventory.js +4 -1
  172. package/src/security/process.js +35 -9
  173. package/src/security/render.js +27 -5
  174. package/src/security/scan.js +24 -9
  175. package/src/security/trust-boundary.js +24 -0
  176. package/src/skills/index.js +64 -0
  177. package/src/telemetry/contracts.js +203 -0
  178. package/src/telemetry/events.js +48 -0
  179. package/src/telemetry/index.js +8 -0
  180. package/src/tools/hydrate.js +35 -0
  181. package/src/tools/index.js +2 -0
  182. package/src/tools/search.js +70 -0
  183. package/src/ui/ansi.js +1 -1
  184. package/src/ui/boot.js +54 -0
  185. package/src/ui/merkle/render.js +1 -0
  186. package/src/ui/runtime-activity.js +192 -0
  187. package/src/ui/theme.js +19 -18
  188. package/test/account-session.test.mjs +36 -0
  189. package/test/app-building-skills.test.mjs +61 -0
  190. package/test/apps-scaffold-contract.test.mjs +56 -0
  191. package/test/capabilities.test.mjs +53 -4
  192. package/test/cli-preferences.test.mjs +46 -0
  193. package/test/cloudflare-connector.test.mjs +96 -0
  194. package/test/cloudflare-runtime.test.mjs +75 -0
  195. package/test/context.test.mjs +144 -0
  196. package/test/deploy-health-scan.test.mjs +67 -0
  197. package/test/error-diagnostics.test.mjs +59 -0
  198. package/test/eval-context.test.mjs +37 -0
  199. package/test/execution-approvals.test.mjs +27 -0
  200. package/test/indexing-provider.test.mjs +29 -0
  201. package/test/jr-dev-skill.test.mjs +26 -0
  202. package/test/knowledge-context-pack.test.mjs +19 -0
  203. package/test/knowledge.test.mjs +1 -1
  204. package/test/local-sessions.test.mjs +42 -0
  205. package/test/local-studio-deploy.test.mjs +83 -0
  206. package/test/merkle-persistence.test.mjs +91 -0
  207. package/test/merkle.test.mjs +77 -3
  208. package/test/model-catalog.test.mjs +43 -0
  209. package/test/models.test.mjs +51 -0
  210. package/test/npm10-lock.test.mjs +29 -0
  211. package/test/ollama.test.mjs +94 -0
  212. package/test/openai-responses.test.mjs +95 -0
  213. package/test/project-config.test.mjs +81 -0
  214. package/test/project-rules.test.mjs +44 -0
  215. package/test/provider-credentials.test.mjs +52 -0
  216. package/test/rehydrate.test.mjs +25 -0
  217. package/test/release-hygiene.test.mjs +34 -0
  218. package/test/repository-snapshot-view.test.mjs +112 -0
  219. package/test/responses-runner.test.mjs +148 -0
  220. package/test/runtime-activity.test.mjs +98 -0
  221. package/test/sdk-worker-contract.test.mjs +68 -0
  222. package/test/security.test.mjs +46 -0
  223. package/test/shell.test.mjs +55 -21
  224. package/test/skills.test.mjs +22 -0
  225. package/test/smoke.mjs +5 -2
  226. package/test/telemetry.test.mjs +79 -0
  227. package/test/theme-portability.test.mjs +14 -0
  228. package/test/tools-search.test.mjs +40 -0
  229. package/test/whoami-resume.test.mjs +56 -0
  230. package/examples/agentsam-tui-ansi.mjs +0 -149
  231. package/src/commands/tui.js +0 -120
  232. package/src/ui/splash-xterm.js +0 -290
  233. package/src/ui/splash.js +0 -426
@@ -0,0 +1,238 @@
1
+ import { createHash } from 'node:crypto';
2
+
3
+ const VIEW_NAMES = new Set(['index', 'files', 'full']);
4
+ const FILTER_FIELDS = Object.freeze([
5
+ 'system', 'package', 'category', 'layer', 'kind', 'language', 'role', 'execution_domain', 'tag', 'path', 'symbol', 'import', 'match',
6
+ ]);
7
+ const DEFAULT_LIMIT = 50;
8
+ const MAX_LIMIT = 500;
9
+ const DEFAULT_FACET_LIMIT = 48;
10
+ const MAX_FACET_LIMIT = 200;
11
+
12
+ function clean(value) {
13
+ return value == null ? '' : String(value).trim();
14
+ }
15
+
16
+ function sha256(value) {
17
+ return `sha256:${createHash('sha256').update(JSON.stringify(value)).digest('hex')}`;
18
+ }
19
+
20
+ function values(input) {
21
+ if (input == null) return [];
22
+ const raw = Array.isArray(input) ? input : [input];
23
+ return [...new Set(raw.map(clean).filter(Boolean))];
24
+ }
25
+
26
+ function boundedInt(value, fallback, max) {
27
+ const number = Number(value);
28
+ if (!Number.isInteger(number) || number < 1) return fallback;
29
+ return Math.min(max, number);
30
+ }
31
+
32
+ function globRegex(glob) {
33
+ const input = clean(glob).replace(/^\.\//, '');
34
+ let source = '^';
35
+ for (let i = 0; i < input.length; i += 1) {
36
+ const char = input[i];
37
+ if (char === '*') {
38
+ if (input[i + 1] === '*') { source += '.*'; i += 1; }
39
+ else source += '[^/]*';
40
+ } else if (char === '?') source += '[^/]';
41
+ else source += /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
42
+ }
43
+ return new RegExp(`${source}$`, 'i');
44
+ }
45
+
46
+ function pathMatches(path, selectors) {
47
+ if (!selectors.length) return true;
48
+ const candidate = clean(path).replace(/^\.\//, '');
49
+ return selectors.some((selector) => {
50
+ const normalized = selector.replace(/^\.\//, '');
51
+ if (normalized.includes('*') || normalized.includes('?')) return globRegex(normalized).test(candidate);
52
+ return candidate === normalized || candidate.startsWith(`${normalized.replace(/\/$/, '')}/`);
53
+ });
54
+ }
55
+
56
+ function scalarMatches(value, selectors) {
57
+ if (!selectors.length) return true;
58
+ const candidate = clean(value).toLowerCase();
59
+ return selectors.some((selector) => candidate === selector.toLowerCase());
60
+ }
61
+
62
+ function listMatches(list, selectors) {
63
+ if (!selectors.length) return true;
64
+ const set = new Set((Array.isArray(list) ? list : []).map((value) => clean(value).toLowerCase()));
65
+ return selectors.some((selector) => set.has(selector.toLowerCase()));
66
+ }
67
+
68
+ function lexicalMatches(entry, terms) {
69
+ if (!terms.length) return true;
70
+ const haystack = [
71
+ entry.path, entry.system, entry.package, entry.category, entry.layer, entry.kind, entry.language, entry.role, entry.execution_domain,
72
+ ...(entry.tags || []), ...(entry.symbols || []), ...(entry.imports || []),
73
+ ].map(clean).join('\n').toLowerCase();
74
+ return terms.every((term) => haystack.includes(term.toLowerCase()));
75
+ }
76
+
77
+ export function normalizeRepositorySnapshotFilters(input = {}) {
78
+ return Object.fromEntries(FILTER_FIELDS.map((field) => [field, values(input[field])]).filter(([, list]) => list.length));
79
+ }
80
+
81
+ export function repositoryFileMatches(entry, filters = {}) {
82
+ const f = normalizeRepositorySnapshotFilters(filters);
83
+ return scalarMatches(entry.system, f.system || [])
84
+ && scalarMatches(entry.package, f.package || [])
85
+ && scalarMatches(entry.category, f.category || [])
86
+ && scalarMatches(entry.layer, f.layer || [])
87
+ && scalarMatches(entry.kind, f.kind || [])
88
+ && scalarMatches(entry.language, f.language || [])
89
+ && scalarMatches(entry.role, f.role || [])
90
+ && scalarMatches(entry.execution_domain, f.execution_domain || [])
91
+ && listMatches(entry.tags, f.tag || [])
92
+ && pathMatches(entry.path, f.path || [])
93
+ && listMatches(entry.symbols, f.symbol || [])
94
+ && listMatches(entry.imports, f.import || [])
95
+ && lexicalMatches(entry, f.match || []);
96
+ }
97
+
98
+ function countFacet(entries, getter, limit) {
99
+ const counts = new Map();
100
+ for (const entry of entries) {
101
+ for (const raw of getter(entry)) {
102
+ const value = clean(raw);
103
+ if (!value) continue;
104
+ counts.set(value, (counts.get(value) || 0) + 1);
105
+ }
106
+ }
107
+ return [...counts.entries()]
108
+ .map(([value, count]) => ({ value, count }))
109
+ .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value))
110
+ .slice(0, limit);
111
+ }
112
+
113
+ export function buildRepositorySnapshotFacets(entries = [], { limit = DEFAULT_FACET_LIMIT } = {}) {
114
+ const bounded = boundedInt(limit, DEFAULT_FACET_LIMIT, MAX_FACET_LIMIT);
115
+ const scalar = (field) => (entry) => entry[field] == null ? [] : [entry[field]];
116
+ return {
117
+ systems: countFacet(entries, scalar('system'), bounded),
118
+ packages: countFacet(entries, scalar('package'), bounded),
119
+ layers: countFacet(entries, scalar('layer'), bounded),
120
+ roles: countFacet(entries, scalar('role'), bounded),
121
+ execution_domains: countFacet(entries, scalar('execution_domain'), bounded),
122
+ kinds: countFacet(entries, scalar('kind'), bounded),
123
+ languages: countFacet(entries, scalar('language'), bounded),
124
+ tags: countFacet(entries, (entry) => entry.tags || [], bounded),
125
+ categories: countFacet(entries, scalar('category'), bounded),
126
+ };
127
+ }
128
+
129
+ function compactEntry(entry) {
130
+ return {
131
+ path: entry.path,
132
+ ...(entry.package ? { package: entry.package } : {}),
133
+ ...(entry.package_root ? { package_root: entry.package_root } : {}),
134
+ ...(entry.system ? { system: entry.system } : {}),
135
+ ...(entry.category ? { category: entry.category } : {}),
136
+ ...(entry.layer ? { layer: entry.layer } : {}),
137
+ ...(entry.kind ? { kind: entry.kind } : {}),
138
+ ...(entry.language ? { language: entry.language } : {}),
139
+ ...(entry.role ? { role: entry.role } : {}),
140
+ ...(entry.execution_domain ? { execution_domain: entry.execution_domain } : {}),
141
+ ...(entry.tags?.length ? { tags: entry.tags } : {}),
142
+ ...(entry.symbols?.length ? { symbols: entry.symbols } : {}),
143
+ ...(entry.imports?.length ? { imports: entry.imports } : {}),
144
+ };
145
+ }
146
+
147
+ function compactTrustBoundary(analysis, limit = 20) {
148
+ const source = analysis?.trust_boundary;
149
+ if (!source) return undefined;
150
+ const findings = Array.isArray(source.findings) ? source.findings : [];
151
+ return {
152
+ trust_boundary: {
153
+ schema_version: source.schema_version,
154
+ analyzer: source.analyzer,
155
+ metadata_root: source.metadata_root,
156
+ complete: source.complete,
157
+ status: source.status,
158
+ ok: source.ok,
159
+ browser_roots: source.browser_roots,
160
+ browser_reachable_files: source.browser_reachable_files,
161
+ ast_files: source.ast_files,
162
+ finding_count: findings.length,
163
+ findings_returned: Math.min(findings.length, limit),
164
+ findings_truncated: findings.length > limit,
165
+ findings: findings.slice(0, limit).map((item) => ({
166
+ kind: item.kind,
167
+ severity: item.severity,
168
+ source: item.source,
169
+ ...(item.target ? { target: item.target } : {}),
170
+ ...(item.specifier ? { specifier: item.specifier } : {}),
171
+ ...(item.env ? { env: item.env } : {}),
172
+ ...(item.repair?.action ? { repair_action: item.repair.action } : {}),
173
+ })),
174
+ },
175
+ };
176
+ }
177
+
178
+ function projectionBase(snapshot) {
179
+ return {
180
+ schema_version: snapshot.schema_version,
181
+ capability: snapshot.capability,
182
+ snapshot_id: snapshot.snapshot_id,
183
+ created_at: snapshot.created_at,
184
+ content_hash: snapshot.content_hash,
185
+ repository: snapshot.repository,
186
+ tree: {
187
+ merkle_root: snapshot.tree?.merkle_root || null,
188
+ metadata_root: snapshot.tree?.metadata_root || null,
189
+ manifest: snapshot.tree?.manifest || null,
190
+ classifier: snapshot.tree?.classifier || null,
191
+ stats: snapshot.tree?.stats || null,
192
+ semantic_stats: snapshot.tree?.semantic_stats || null,
193
+ },
194
+ ...(compactTrustBoundary(snapshot.analysis) ? { analysis: compactTrustBoundary(snapshot.analysis) } : {}),
195
+ };
196
+ }
197
+
198
+ /**
199
+ * Produce a bounded, deterministic view over a canonical repository.snapshot.
200
+ * The canonical content_hash / Merkle identities are carried through unchanged;
201
+ * projection_hash identifies only this derived view and is never an authority root.
202
+ */
203
+ export function projectRepositorySnapshot(snapshot, options = {}) {
204
+ if (!snapshot || snapshot.capability !== 'repository.snapshot' || !snapshot.tree) {
205
+ throw new Error('repository_snapshot_required');
206
+ }
207
+ const view = clean(options.view || 'index').toLowerCase();
208
+ if (!VIEW_NAMES.has(view)) throw new Error(`repository_snapshot_view_invalid:${view}`);
209
+ if (view === 'full') return snapshot;
210
+
211
+ const files = Array.isArray(snapshot.tree.files) ? snapshot.tree.files : [];
212
+ const filters = normalizeRepositorySnapshotFilters(options.filters || options);
213
+ const filtered = files.filter((entry) => repositoryFileMatches(entry, filters));
214
+ const limit = boundedInt(options.limit, DEFAULT_LIMIT, MAX_LIMIT);
215
+ const facetLimit = boundedInt(options.facetLimit ?? options.facet_limit, DEFAULT_FACET_LIMIT, MAX_FACET_LIMIT);
216
+ const base = projectionBase(snapshot);
217
+ const result = {
218
+ ...base,
219
+ projection: {
220
+ format: 'agentsam-repository-view',
221
+ version: 1,
222
+ view,
223
+ source_snapshot_id: snapshot.snapshot_id,
224
+ source_content_hash: snapshot.content_hash,
225
+ filters,
226
+ matched: filtered.length,
227
+ returned: view === 'files' ? Math.min(filtered.length, limit) : 0,
228
+ truncated: view === 'files' && filtered.length > limit,
229
+ limit: view === 'files' ? limit : 0,
230
+ },
231
+ facets: buildRepositorySnapshotFacets(filtered, { limit: facetLimit }),
232
+ ...(view === 'files' ? { files: filtered.slice(0, limit).map(compactEntry) } : {}),
233
+ };
234
+ result.projection.projection_hash = sha256({ ...result, projection: { ...result.projection, projection_hash: undefined } });
235
+ return result;
236
+ }
237
+
238
+ export const REPOSITORY_SNAPSHOT_VIEWS = Object.freeze([...VIEW_NAMES]);
@@ -5,7 +5,10 @@ import { promisify } from 'node:util';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { createHash } from 'node:crypto';
7
7
  import { resolveGitContext } from '../lib/git-context.js';
8
+ import { getRepositoryId, tryReadProjectConfig } from '../lib/project-config.js';
8
9
  import { buildMerkleTree } from '../lib/merkle/index.js';
10
+ import { gitIgnoredPaths } from '../lib/merkle/git-ignore.js';
11
+ import { analyzeExecutionBoundaries } from '../indexing/execution-boundary.js';
9
12
  import { showLatestDeployReceipt } from '../lib/deploy-receipt/index.js';
10
13
  import { CONFIG_PATH, canonical, readConfig, scopeKey } from '../knowledge/config.js';
11
14
  import { openSqliteStore } from '../knowledge/stores/sqlite.js';
@@ -24,16 +27,6 @@ function providerForHost(host) {
24
27
  return value ? 'git' : null;
25
28
  }
26
29
 
27
- async function gitIgnoredPaths(root) {
28
- const result = await execute('git', ['status', '--porcelain=v1', '--ignored=matching'], {
29
- cwd: root,
30
- maxBuffer: 16 * 1024 * 1024,
31
- });
32
- return [...new Set(result.stdout.split('\n')
33
- .filter((line) => line.startsWith('!! '))
34
- .map((line) => line.slice(3).trim().replace(/\/$/, ''))
35
- .filter((value) => value && !value.includes('\\') && !value.split('/').includes('..')))];
36
- }
37
30
 
38
31
  async function runRepositoryIntelligence(root, churnDays) {
39
32
  const pythonRoot = fileURLToPath(new URL('../../python', import.meta.url));
@@ -121,21 +114,29 @@ export async function repositorySnapshot({ cwd = process.cwd(), churnDays = 30 }
121
114
  const git = resolveGitContext({ cwd });
122
115
  const root = git.root;
123
116
  const ignored = await gitIgnoredPaths(root);
117
+ const projectConfig = tryReadProjectConfig(root);
124
118
  const [intelligence, merkle, knowledge, deployReceipt] = await Promise.all([
125
119
  runRepositoryIntelligence(root, churnDays),
126
- buildMerkleTree(root, { exclude: ignored }),
120
+ buildMerkleTree(root, { exclude: ignored, semantic: true }),
127
121
  readKnowledgeState(root),
128
122
  showLatestDeployReceipt({ root }),
129
123
  ]);
130
124
  const provider = providerForHost(git.remoteHost);
131
- const portableRepositoryId = provider && git.repoFullName ? `${provider}:${git.repoFullName}` : null;
132
- const repositoryId = knowledge.repository_id || portableRepositoryId;
125
+ const portableRepositoryId = provider && git.repoFullName
126
+ ? `${provider}:${provider === 'github' ? git.repoFullName.toLowerCase() : git.repoFullName}`
127
+ : null;
128
+ const projectRepositoryId = getRepositoryId(projectConfig);
129
+ if (projectRepositoryId && knowledge.repository_id && projectRepositoryId !== knowledge.repository_id) {
130
+ throw new Error(`repository_identity_mismatch: project=${projectRepositoryId} knowledge=${knowledge.repository_id}`);
131
+ }
132
+ const repositoryId = projectRepositoryId || knowledge.repository_id || portableRepositoryId;
133
133
  const packages = readPackageInventory(root, intelligence.manifests || []);
134
+ const trustBoundary = analyzeExecutionBoundaries(merkle.semantic);
134
135
 
135
136
  const evidence = {
136
137
  repository: {
137
138
  repository_id: repositoryId,
138
- identity_source: knowledge.repository_id ? 'knowledge-config' : portableRepositoryId ? 'git-remote' : 'unresolved',
139
+ identity_source: projectRepositoryId ? 'project-config' : knowledge.repository_id ? 'knowledge-config' : portableRepositoryId ? 'git-remote' : 'unresolved',
139
140
  provider,
140
141
  full_name: git.repoFullName,
141
142
  remote_url: git.remoteUrl || null,
@@ -145,10 +146,20 @@ export async function repositorySnapshot({ cwd = process.cwd(), churnDays = 30 }
145
146
  },
146
147
  tree: {
147
148
  merkle_root: merkle.rootHash,
149
+ metadata_root: merkle.semantic.rootHash,
150
+ manifest: {
151
+ format: merkle.format,
152
+ version: merkle.version,
153
+ hash_algorithm: merkle.algorithm,
154
+ policy_hash: merkle.policyHash,
155
+ },
156
+ classifier: merkle.semantic.classifier,
148
157
  stats: merkle.stats,
158
+ semantic_stats: merkle.semantic.stats,
149
159
  paths: merkle.entries
150
160
  .filter((entry) => entry.type === 'file' || entry.type === 'symlink')
151
161
  .map((entry) => entry.path),
162
+ files: merkle.semantic.entries,
152
163
  },
153
164
  intelligence: {
154
165
  summary: intelligence.summary,
@@ -159,6 +170,9 @@ export async function repositorySnapshot({ cwd = process.cwd(), churnDays = 30 }
159
170
  },
160
171
  packages,
161
172
  knowledge,
173
+ analysis: {
174
+ trust_boundary: trustBoundary,
175
+ },
162
176
  deploy: deployReceipt ? {
163
177
  status: deployReceipt.status,
164
178
  root_hash: deployReceipt.root_hash,