@inneranimalmedia/agentsam-sdk 2.6.8 → 2.6.10

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 (203) hide show
  1. package/README.md +18 -0
  2. package/apps/README.md +25 -0
  3. package/apps/cad-creator/shared/cad/package.json +1 -1
  4. package/apps/client-cms-editor/backend/package.json +5 -7
  5. package/apps/client-cms-editor/frontend/package.json +5 -7
  6. package/apps/client-cms-editor/frontend/tsconfig.json +27 -8
  7. package/apps/client-cms-editor/package-lock.json +8 -8
  8. package/apps/client-cms-editor/package.json +1 -1
  9. package/apps/client-cms-editor/shared/cms/package.json +3 -5
  10. package/docs/LOCAL_ENGINE_COMPUTE_AUDIT_PLAN.md +299 -0
  11. package/docs/architecture/HOOKS-MODELS-SELF-HOSTING-PLAN.md +199 -0
  12. package/examples/hyperspace-demo/index.html +16 -0
  13. package/examples/hyperspace-demo/package.json +21 -0
  14. package/examples/hyperspace-demo/src/main.tsx +160 -0
  15. package/examples/hyperspace-demo/tsconfig.json +9 -0
  16. package/examples/hyperspace-demo/vite.config.ts +24 -0
  17. package/migrations/runtime/0005_agentsam_hooks.sql +87 -0
  18. package/package.json +37 -10
  19. package/packages/agentsam-brand/package.json +1 -1
  20. package/packages/agentsam-brand/src/core/v2/compile.js +1 -1
  21. package/packages/agentsam-brand/src/core/v2/schema.js +1 -1
  22. package/packages/agentsam-contracts/package.json +71 -20
  23. package/packages/agentsam-contracts/scripts/build.mjs +18 -0
  24. package/packages/agentsam-contracts/src/errors.ts +19 -10
  25. package/packages/agentsam-contracts/test/contracts.test.mjs +4 -1
  26. package/packages/agentsam-contracts/test/repository.test.mjs +4 -1
  27. package/packages/agentsam-contracts/tsconfig.build.json +10 -0
  28. package/packages/agentsam-errors/package.json +13 -4
  29. package/packages/agentsam-errors/protocol/agentsam.error.v2.schema.json +821 -0
  30. package/packages/agentsam-errors/protocol/error-catalog.json +3512 -0
  31. package/packages/agentsam-errors/src/envelope.js +63 -11
  32. package/packages/agentsam-errors/src/error.js +6 -0
  33. package/packages/agentsam-errors/src/fingerprint.js +9 -1
  34. package/packages/agentsam-errors/src/normalize.js +51 -6
  35. package/packages/agentsam-errors/src/recovery.js +9 -35
  36. package/packages/agentsam-errors/src/transport.js +6 -1
  37. package/packages/agentsam-errors/src/vocabulary.js +515 -126
  38. package/packages/agentsam-errors/test/catalog-v2.test.mjs +106 -0
  39. package/packages/agentsam-errors/types/index.d.ts +157 -0
  40. package/packages/agentsam-goap/package.json +2 -2
  41. package/packages/agentsam-hooks/README.md +206 -0
  42. package/packages/agentsam-hooks/bin/agentsam-hooks.mjs +64 -0
  43. package/packages/agentsam-hooks/bindings/go/command.go +55 -0
  44. package/packages/agentsam-hooks/bindings/go/errors_generated.go +402 -0
  45. package/packages/agentsam-hooks/bindings/go/go.mod +3 -0
  46. package/packages/agentsam-hooks/bindings/go/hooks.go +285 -0
  47. package/packages/agentsam-hooks/bindings/go/hooks_test.go +48 -0
  48. package/packages/agentsam-hooks/bindings/rust/Cargo.toml +11 -0
  49. package/packages/agentsam-hooks/bindings/rust/src/errors_generated.rs +396 -0
  50. package/packages/agentsam-hooks/bindings/rust/src/lib.rs +397 -0
  51. package/packages/agentsam-hooks/package.json +69 -0
  52. package/packages/agentsam-hooks/schema/manifest.json +54 -0
  53. package/packages/agentsam-hooks/scripts/clean-protocol.mjs +17 -0
  54. package/packages/agentsam-hooks/scripts/sync-protocol.mjs +27 -0
  55. package/packages/agentsam-hooks/src/adapters/agents.js +82 -0
  56. package/packages/agentsam-hooks/src/adapters/callback.js +4 -0
  57. package/packages/agentsam-hooks/src/adapters/capability.js +122 -0
  58. package/packages/agentsam-hooks/src/adapters/command.js +104 -0
  59. package/packages/agentsam-hooks/src/adapters/composite.js +31 -0
  60. package/packages/agentsam-hooks/src/adapters/http.js +58 -0
  61. package/packages/agentsam-hooks/src/adapters/lsp.js +116 -0
  62. package/packages/agentsam-hooks/src/adapters/mcp.js +76 -0
  63. package/packages/agentsam-hooks/src/adapters/provider.js +129 -0
  64. package/packages/agentsam-hooks/src/config.js +143 -0
  65. package/packages/agentsam-hooks/src/contracts.js +284 -0
  66. package/packages/agentsam-hooks/src/errors-generated.js +2732 -0
  67. package/packages/agentsam-hooks/src/index.d.ts +193 -0
  68. package/packages/agentsam-hooks/src/index.js +14 -0
  69. package/packages/agentsam-hooks/src/policy.js +34 -0
  70. package/packages/agentsam-hooks/src/runtime.js +312 -0
  71. package/packages/agentsam-hooks/src/store.js +264 -0
  72. package/packages/agentsam-hooks/test/adapters.test.mjs +216 -0
  73. package/packages/agentsam-hooks/test/runtime.test.mjs +258 -0
  74. package/packages/agentsam-knowledge/package.json +3 -2
  75. package/packages/agentsam-nav/package.json +1 -1
  76. package/packages/agentsam-queue-control/package.json +1 -1
  77. package/packages/agentsam-rapid-rust/src/main.rs +1 -0
  78. package/packages/agentsam-repository/package.json +3 -2
  79. package/packages/agentsam-vault/package.json +8 -5
  80. package/packages/agentsam-workbench/package.json +45 -18
  81. package/packages/connectors/cloudflare/package.json +1 -1
  82. package/packages/heuristic-theme/package.json +1 -1
  83. package/packages/identity/package.json +3 -2
  84. package/packages/identity/tests/lane1-packed-consumer.test.mjs +9 -1
  85. package/packages/providers/completeful/package.json +1 -1
  86. package/packages/theme-church-site/README.md +2 -2
  87. package/packages/theme-church-site/package.json +2 -3
  88. package/packages/theme-church-site/src/index.js +1 -1
  89. package/packages/theme-church-site/test/theme.test.mjs +1 -1
  90. package/packages/theme-companions-site/README.md +2 -2
  91. package/packages/theme-companions-site/package.json +2 -3
  92. package/packages/theme-companions-site/src/index.js +1 -1
  93. package/packages/theme-companions-site/test/theme.test.mjs +1 -1
  94. package/packages/theme-floors-site/README.md +2 -2
  95. package/packages/theme-floors-site/package.json +2 -3
  96. package/packages/theme-floors-site/src/index.js +1 -1
  97. package/packages/theme-floors-site/test/theme.test.mjs +1 -1
  98. package/packages/theme-fuelnfree-site/README.md +2 -2
  99. package/packages/theme-fuelnfree-site/package.json +2 -3
  100. package/packages/theme-fuelnfree-site/src/index.js +1 -1
  101. package/packages/theme-fuelnfree-site/test/theme.test.mjs +1 -1
  102. package/packages/theme-handyman-site/README.md +2 -2
  103. package/packages/theme-handyman-site/package.json +2 -3
  104. package/packages/theme-handyman-site/src/index.js +1 -1
  105. package/packages/theme-handyman-site/test/theme.test.mjs +1 -1
  106. package/packages/theme-insurance-site/README.md +2 -2
  107. package/packages/theme-insurance-site/package.json +2 -3
  108. package/packages/theme-insurance-site/src/index.js +1 -1
  109. package/packages/theme-insurance-site/test/theme.test.mjs +1 -1
  110. package/packages/theme-scenes/package.json +1 -1
  111. package/packages/theme-shinshu-site/README.md +2 -2
  112. package/packages/theme-shinshu-site/package.json +2 -3
  113. package/packages/theme-shinshu-site/src/index.js +1 -1
  114. package/packages/theme-shinshu-site/test/theme.test.mjs +1 -1
  115. package/packages/work-graph/package.json +3 -2
  116. package/protocol/apps/agentsam.app.v1.schema.json +1 -1
  117. package/protocol/apps/agentsam.host-install.v1.schema.json +1 -1
  118. package/protocol/assets/asset-record.v1.schema.json +1 -1
  119. package/protocol/brand/brandpack.v1.schema.json +1 -1
  120. package/protocol/capabilities/capability-manifest.schema.json +1 -1
  121. package/protocol/capabilities/knowledge-search-input.schema.json +1 -0
  122. package/protocol/credentials/issue.v1.schema.json +1 -1
  123. package/protocol/database/agentsam.schema-pack.v1.schema.json +1 -1
  124. package/protocol/database/connection.v1.schema.json +1 -1
  125. package/protocol/embeddings/embedding-profile.v1.schema.json +1 -1
  126. package/protocol/errors/README.md +36 -0
  127. package/protocol/errors/agentsam.error.v2.schema.json +821 -0
  128. package/protocol/errors/error-catalog.json +3506 -158
  129. package/protocol/errors/error-envelope.schema.json +195 -52
  130. package/protocol/errors/recovery.v1.schema.json +1 -1
  131. package/protocol/features/agentsam.feature.v1.schema.json +1 -1
  132. package/protocol/hooks/README.md +18 -0
  133. package/protocol/hooks/agentsam.hook.output.v1.schema.json +27 -0
  134. package/protocol/hooks/agentsam.hook.receipt.v1.schema.json +61 -0
  135. package/protocol/hooks/agentsam.hook.v1.schema.json +35 -0
  136. package/protocol/hooks/agentsam.hooks.config.v1.schema.json +78 -0
  137. package/protocol/local-engine/benchmark.v1.schema.json +14 -0
  138. package/protocol/local-engine/engine.v1.schema.json +17 -0
  139. package/protocol/models/model-inventory-v2.schema.json +1 -1
  140. package/protocol/models/provider-model-snapshot.v1.schema.json +22 -0
  141. package/protocol/rpc/v1/errors.proto +1 -0
  142. package/protocol/runtime/agentsam.runtime.v1.schema.json +1 -1
  143. package/protocol/runtime/workspace-fs.v1.schema.json +1 -1
  144. package/protocol/sam/activity.v1.schema.json +1 -1
  145. package/protocol/sam/answer.v1.schema.json +1 -1
  146. package/protocol/sam/calibration.v1.schema.json +1 -1
  147. package/protocol/sam/decision-receipt.v1.schema.json +1 -1
  148. package/protocol/sam/evaluation.v1.schema.json +1 -1
  149. package/protocol/sam/operation.schema.json +1 -1
  150. package/protocol/sam/outcome.v1.schema.json +1 -1
  151. package/protocol/sam/question.v1.schema.json +1 -1
  152. package/protocol/sam/result.schema.json +1 -1
  153. package/protocol/sam/state.v1.schema.json +1 -1
  154. package/protocol/scoring/scorecard.v1.schema.json +1 -1
  155. package/protocol/skills/agentsam.interaction.v1.schema.json +1 -1
  156. package/protocol/skills/agentsam.skill.v1.schema.json +1 -1
  157. package/protocol/theme-refinery/product-metadata.schema.json +1 -1
  158. package/protocol/theme-refinery/reconciliation-receipt.schema.json +1 -1
  159. package/protocol/ui/icon.v1.schema.json +1 -1
  160. package/python/README.md +20 -0
  161. package/python/agentsam_sdk/errors/__init__.py +4 -0
  162. package/python/agentsam_sdk/errors/vocabulary.py +2730 -0
  163. package/python/agentsam_sdk/hooks/__init__.py +32 -0
  164. package/python/agentsam_sdk/hooks/adapters.py +132 -0
  165. package/python/agentsam_sdk/hooks/contracts.py +121 -0
  166. package/python/agentsam_sdk/hooks/runtime.py +253 -0
  167. package/python/tests/test_hooks.py +115 -0
  168. package/src/agent/responses-runner.js +145 -1
  169. package/src/cli/command-catalog.js +2 -0
  170. package/src/cli.js +19 -0
  171. package/src/commands/app.js +10 -0
  172. package/src/commands/cheat-sheet.js +1 -0
  173. package/src/commands/engine.js +10 -0
  174. package/src/commands/hooks.js +260 -0
  175. package/src/commands/machine.js +71 -38
  176. package/src/commands/models.js +1 -1
  177. package/src/commands/package.js +10 -3
  178. package/src/commands/runtime.js +25 -10
  179. package/src/commands/setup.js +30 -6
  180. package/src/commands/shell.js +114 -9
  181. package/src/commands/terminal.js +20 -8
  182. package/src/errors/contract.js +4 -4
  183. package/src/errors/diagnostic.js +10 -10
  184. package/src/errors/index.js +1 -1
  185. package/src/index.js +52 -0
  186. package/src/indexing/ingest/materials.js +237 -30
  187. package/src/knowledge/service/grpc-codec.js +2 -0
  188. package/src/lib/setup/runtime.js +4 -2
  189. package/src/lib/slash-commands.js +1 -0
  190. package/src/local/runtime-store.js +46 -0
  191. package/src/local-engine/assets.js +37 -0
  192. package/src/local-engine/audit.js +133 -0
  193. package/src/local-engine/benchmark.js +26 -0
  194. package/src/local-engine/contracts.js +101 -0
  195. package/src/local-engine/engines.js +142 -0
  196. package/src/local-engine/index.js +18 -0
  197. package/src/local-engine/runtime.js +39 -0
  198. package/src/models/catalog.js +0 -49
  199. package/src/models/inventory-core.js +69 -0
  200. package/src/plugins/index.js +8 -1
  201. package/src/plugins/registry.js +56 -0
  202. package/src/rpc/generated/errors_pb.js +49 -1
  203. package/src/tools/runtime.js +13 -5
@@ -6,7 +6,7 @@ import {
6
6
  normalizeError,
7
7
  redactErrorValue,
8
8
  redactString,
9
- } from '../../packages/agentsam-errors/src/index.js';
9
+ } from '@inneranimalmedia/agentsam-errors';
10
10
 
11
11
  const DEFAULT_MAX_DETAIL_CHARS = 12_000;
12
12
  function clean(value) { return value == null ? '' : String(value).trim(); }
@@ -181,16 +181,16 @@ export function diagnosticFromError(error, fallback = {}) {
181
181
  if (error?.diagnostic) return error.diagnostic;
182
182
  const envelope = error?.envelope || (error?.ok === false && error?.schema_version ? error : null);
183
183
  if (envelope) return legacyDiagnosticFromEnvelope(envelope, fallback);
184
- return Object.freeze({
185
- schema_version: 1,
186
- source: fallback.source || 'agentsam',
187
- kind: fallback.kind || 'runtime_error',
188
- code: clean(error?.code || fallback.code) || 'runtime_error',
189
- message: redactString(error?.message || error || fallback.message || 'Unknown error', 4_000),
190
- http_status: Number(error?.status || fallback.status || 0) || null,
191
- retriable: Boolean(fallback.retriable),
192
- retry_strategy: fallback.retry_strategy || 'inspect_error',
184
+ const canonical = normalizeError(error, {
185
+ reason: fallback.reason,
186
+ domain: fallback.domain || 'runtime',
187
+ stage: fallback.stage,
188
+ feature: fallback.feature,
189
+ failure_behavior: fallback.failure_behavior,
190
+ side_effect_state: fallback.side_effect_state,
191
+ source: { kind: 'runtime', name: fallback.source || 'agentsam-sdk' },
193
192
  });
193
+ return legacyDiagnosticFromEnvelope(canonical, fallback);
194
194
  }
195
195
 
196
196
  export function renderDiagnosticError(error) {
@@ -1,4 +1,4 @@
1
- export * from '../../packages/agentsam-errors/src/index.js';
1
+ export * from '@inneranimalmedia/agentsam-errors';
2
2
 
3
3
  export {
4
4
  AgentSamDiagnosticError,
package/src/index.js CHANGED
@@ -25,6 +25,37 @@ export {
25
25
  suggestCliCommands,
26
26
  } from './cli/command-catalog.js';
27
27
  export { routeIntent } from './lib/router.js';
28
+ export {
29
+ HOOK_PROTOCOL_SCHEMA,
30
+ HOOK_CONFIG_SCHEMA,
31
+ HOOK_RECEIPT_SCHEMA,
32
+ HOOK_EVENTS,
33
+ PERMISSION_DECISIONS,
34
+ ERROR_HANDLING_DECISIONS,
35
+ HOOK_FAILURE_MODES,
36
+ AgentSamHooks,
37
+ HookExecutionError,
38
+ HookPermissionError,
39
+ normalizeHookEvent,
40
+ normalizeHookOutput,
41
+ createHookEnvelope,
42
+ createHookRuntime,
43
+ createHookRuntimeFromConfig,
44
+ createHookStore,
45
+ registerStoredHooks,
46
+ ensureHookRuntime,
47
+ findHookConfig,
48
+ loadHookConfig,
49
+ createCallbackHookAdapter,
50
+ createCommandHookAdapter,
51
+ createHttpHookAdapter,
52
+ createHookedProviderAdapter,
53
+ createHookedCapabilityAdapter,
54
+ createCompositeCapabilityAdapter,
55
+ createMcpCapabilityAdapter,
56
+ createLspCapabilityAdapter,
57
+ createAgentCapabilityAdapter,
58
+ } from '../packages/agentsam-hooks/src/index.js';
28
59
  export { searchToolCards, toToolCard, hydrateToolSchemas } from './tools/index.js';
29
60
  export {
30
61
  AGENTSAM_PLUGIN_SCHEMA_VERSION,
@@ -36,6 +67,7 @@ export {
36
67
  listPlugins,
37
68
  listPluginTools,
38
69
  recordPluginHealthCheck,
70
+ recordAiPluginModelDiscovery,
39
71
  recordToolCall,
40
72
  createPluginRuntime,
41
73
  createPluginCapabilityAdapter,
@@ -77,6 +109,26 @@ export {
77
109
  terminalExec,
78
110
  } from './capabilities/index.js';
79
111
  export { getPreset, listPresets, resolvePreset, getAddon, listAddons } from './presets/index.js';
112
+ export {
113
+ LOCAL_ENGINE_SCHEMA,
114
+ LOCAL_ENGINE_BENCHMARK_SCHEMA,
115
+ ENGINE_CAPABILITIES,
116
+ LocalEngineRegistry,
117
+ createLocalEngineRegistry,
118
+ createOllamaEngine,
119
+ createMlxLmEngine,
120
+ createLlamaCppEngine,
121
+ auditLocalCompute,
122
+ auditHost,
123
+ auditAppleHost,
124
+ auditTools,
125
+ auditAssets,
126
+ adoptAsset,
127
+ readAssetManifest,
128
+ assetManifestPath,
129
+ benchmarkEngine,
130
+ validateStructuredOutput,
131
+ } from './local-engine/index.js';
80
132
  export {
81
133
  COMPANY_REPOSITORY_GRAPH_SCHEMA_VERSION,
82
134
  REPOSITORY_STATUSES,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Material intake for codebaseindex.ingest — paths pasted/dropped into the CLI.
3
- * Handles archives (tar/zip), sites/builds, HTML, images, GLB, and mixed trees.
3
+ * Archives are inspected first and selectively mined instead of blindly unpacked.
4
4
  */
5
5
 
6
6
  import fs from 'node:fs';
@@ -12,6 +12,20 @@ import { isCodeSourceExtension, isDocumentSourceExtension } from '../../../packa
12
12
  const ARCHIVE_EXT = new Set(['.zip', '.tar', '.tgz', '.gz', '.tar.gz', '.tbz2', '.tar.bz2']);
13
13
  const IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico', '.bmp', '.avif']);
14
14
  const MODEL_3D_EXT = new Set(['.glb', '.gltf', '.obj', '.fbx', '.stl', '.usdz']);
15
+ const VIDEO_EXT = new Set(['.mp4', '.mov', '.m4v', '.webm', '.mkv', '.avi', '.mpeg', '.mpg']);
16
+ const AUDIO_EXT = new Set(['.mp3', '.wav', '.m4a', '.aac', '.ogg', '.flac']);
17
+ const TEXT_BASENAMES = new Set([
18
+ 'readme', 'license', 'licence', 'dockerfile', 'makefile', 'procfile', 'gemfile', 'rakefile',
19
+ 'cargo.lock', 'package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', '.gitignore', '.npmrc', '.editorconfig',
20
+ ]);
21
+ const DEFAULT_ARCHIVE_POLICY = Object.freeze({
22
+ maxEntries: 10_000,
23
+ maxFiles: 2_000,
24
+ maxEntryBytes: 8 * 1024 * 1024,
25
+ maxTotalBytes: 128 * 1024 * 1024,
26
+ includeAssets: false,
27
+ });
28
+
15
29
  function clean(value) {
16
30
  return value == null ? '' : String(value).trim();
17
31
  }
@@ -43,9 +57,7 @@ export function parsePastedPaths(text) {
43
57
  return [...new Set(paths)];
44
58
  }
45
59
 
46
- /**
47
- * @param {string} filePath
48
- */
60
+ /** @param {string} filePath */
49
61
  export function classifyMaterial(filePath) {
50
62
  const resolved = path.resolve(filePath);
51
63
  const ext = extOf(resolved);
@@ -79,37 +91,237 @@ function hashFile(filePath) {
79
91
 
80
92
  function hasBin(name) {
81
93
  const result = spawnSync(name, ['--help'], { stdio: 'ignore' });
82
- // Some tools exit non-zero on --help; presence of spawn failure matters more.
83
94
  return result.error == null;
84
95
  }
85
96
 
97
+ function archiveFormat(archivePath) {
98
+ const lower = archivePath.toLowerCase();
99
+ if (lower.endsWith('.zip')) return 'zip';
100
+ if (lower.endsWith('.tar') || lower.endsWith('.tar.gz') || lower.endsWith('.tgz')
101
+ || lower.endsWith('.tar.bz2') || lower.endsWith('.tbz2')) return 'tar';
102
+ if (lower.endsWith('.gz')) return 'gzip';
103
+ return null;
104
+ }
105
+
106
+ function archiveEntryKind(entryPath) {
107
+ const normalized = String(entryPath || '').replace(/\\/g, '/');
108
+ const ext = path.posix.extname(normalized.toLowerCase());
109
+ const base = path.posix.basename(normalized).toLowerCase();
110
+ if (IMAGE_EXT.has(ext)) return 'image';
111
+ if (MODEL_3D_EXT.has(ext)) return 'model3d';
112
+ if (VIDEO_EXT.has(ext)) return 'video';
113
+ if (AUDIO_EXT.has(ext)) return 'audio';
114
+ if (isDocumentSourceExtension(ext) || TEXT_BASENAMES.has(base)) return 'document';
115
+ if (isCodeSourceExtension(ext)) return 'code';
116
+ return 'file';
117
+ }
118
+
119
+ function isSafeArchivePath(entryPath) {
120
+ const raw = String(entryPath || '').replace(/\\/g, '/');
121
+ if (!raw || raw.includes('\0') || raw.startsWith('/') || /^[a-zA-Z]:\//.test(raw)) return false;
122
+ const parts = raw.split('/').filter((part) => part && part !== '.');
123
+ return !parts.includes('..');
124
+ }
125
+
126
+ function shouldMineArchiveEntry(entry, policy) {
127
+ if (!entry.safe) return { selected: false, reason: 'unsafe_path' };
128
+ if (entry.link) return { selected: false, reason: 'link_entry' };
129
+ if (entry.directory) return { selected: false, reason: 'directory' };
130
+ if (Number.isFinite(entry.bytes) && entry.bytes > policy.maxEntryBytes) {
131
+ return { selected: false, reason: 'entry_too_large' };
132
+ }
133
+ if (entry.kind === 'code' || entry.kind === 'document') return { selected: true, reason: 'indexable_text' };
134
+ if (policy.includeAssets && ['image', 'model3d'].includes(entry.kind)) return { selected: true, reason: 'requested_asset' };
135
+ return { selected: false, reason: ['image', 'model3d', 'video', 'audio'].includes(entry.kind) ? 'asset_inventory_only' : 'non_indexable' };
136
+ }
137
+
138
+ function commandText(command, args, maxBuffer = 16 * 1024 * 1024) {
139
+ return execFileSync(command, args, {
140
+ encoding: 'utf8',
141
+ maxBuffer,
142
+ env: { ...process.env, LC_ALL: 'C' },
143
+ stdio: ['ignore', 'pipe', 'pipe'],
144
+ });
145
+ }
146
+
147
+ function zipSizeMap(archivePath) {
148
+ if (!hasBin('zipinfo')) return new Map();
149
+ const map = new Map();
150
+ const lines = commandText('zipinfo', ['-l', archivePath], 32 * 1024 * 1024).split(/\r?\n/);
151
+ for (const line of lines) {
152
+ const match = line.match(/^(\S+)\s+\S+\s+\S+\s+(\d+)\s+\S+\s+\d+\s+\S+\s+\S+\s+\S+\s+(.*)$/);
153
+ if (!match) continue;
154
+ map.set(match[3], { bytes: Number(match[2]), link: match[1].startsWith('l') });
155
+ }
156
+ return map;
157
+ }
158
+
86
159
  /**
87
- * Extract archive into destDir. Uses host tar/unzip (no extra npm deps).
160
+ * Inspect an archive without expanding it to disk.
161
+ * @param {string} archivePath
162
+ * @param {Partial<typeof DEFAULT_ARCHIVE_POLICY>} [options]
163
+ */
164
+ export function inspectArchive(archivePath, options = {}) {
165
+ const resolved = path.resolve(archivePath);
166
+ const policy = { ...DEFAULT_ARCHIVE_POLICY, ...options };
167
+ const format = archiveFormat(resolved);
168
+ if (!format) throw new Error(`unsupported_archive:${path.basename(resolved)}`);
169
+ if (!fs.existsSync(resolved)) throw new Error(`archive_missing:${resolved}`);
170
+
171
+ let names = [];
172
+ let zipMeta = new Map();
173
+ let tarLinks = new Set();
174
+ if (format === 'zip') {
175
+ if (!hasBin('unzip')) throw new Error('unzip_unavailable: install unzip to inspect .zip materials');
176
+ names = commandText('unzip', ['-Z1', resolved], 32 * 1024 * 1024).split(/\r?\n/).filter(Boolean);
177
+ zipMeta = zipSizeMap(resolved);
178
+ } else if (format === 'tar') {
179
+ if (!hasBin('tar')) throw new Error('tar_unavailable: install tar to inspect tar materials');
180
+ names = commandText('tar', ['-tf', resolved], 32 * 1024 * 1024).split(/\r?\n/).filter(Boolean);
181
+ const verbose = commandText('tar', ['-tvf', resolved], 32 * 1024 * 1024).split(/\r?\n/);
182
+ for (const line of verbose) {
183
+ if (!(line.startsWith('l') || line.startsWith('h'))) continue;
184
+ const marker = line.includes(' -> ') ? line.split(' -> ')[0] : line;
185
+ const name = names.find((candidate) => marker.endsWith(candidate));
186
+ if (name) tarLinks.add(name);
187
+ }
188
+ } else {
189
+ if (!hasBin('gzip')) throw new Error('gzip_unavailable: install gzip to inspect .gz materials');
190
+ names = [path.basename(resolved, '.gz') || 'payload'];
191
+ }
192
+
193
+ if (names.length > policy.maxEntries) {
194
+ const error = new Error(`archive_entry_limit_exceeded:${names.length}>${policy.maxEntries}`);
195
+ error.code = 'AGENTSAM_ARCHIVE_ENTRY_LIMIT';
196
+ throw error;
197
+ }
198
+
199
+ const entries = names.map((name) => {
200
+ const meta = zipMeta.get(name) || {};
201
+ const directory = name.endsWith('/');
202
+ const entry = {
203
+ path: name,
204
+ bytes: Number.isFinite(meta.bytes) ? meta.bytes : null,
205
+ kind: directory ? 'directory' : archiveEntryKind(name),
206
+ directory,
207
+ link: Boolean(meta.link || tarLinks.has(name)),
208
+ safe: isSafeArchivePath(name),
209
+ };
210
+ return { ...entry, ...shouldMineArchiveEntry(entry, policy) };
211
+ });
212
+
213
+ const unsafe = entries.filter((entry) => !entry.safe);
214
+ if (unsafe.length) {
215
+ const error = new Error(`archive_unsafe_paths:${unsafe.slice(0, 5).map((entry) => entry.path).join(',')}`);
216
+ error.code = 'AGENTSAM_ARCHIVE_UNSAFE_PATH';
217
+ throw error;
218
+ }
219
+
220
+ const selected = entries.filter((entry) => entry.selected);
221
+ const selectedKnownBytes = selected.reduce((sum, entry) => sum + (entry.bytes || 0), 0);
222
+ const byKind = {};
223
+ for (const entry of entries) byKind[entry.kind] = (byKind[entry.kind] || 0) + 1;
224
+
225
+ return {
226
+ schema: 'agentsam.archive.inspect.v1',
227
+ archive: resolved,
228
+ format,
229
+ compressed_bytes: fs.statSync(resolved).size,
230
+ entries_total: entries.length,
231
+ selected_total: selected.length,
232
+ selected_known_bytes: selectedKnownBytes,
233
+ by_kind: byKind,
234
+ policy,
235
+ entries,
236
+ };
237
+ }
238
+
239
+ function readArchiveEntry(archivePath, format, entry, maxBytes) {
240
+ const maxBuffer = Math.max(1024 * 1024, maxBytes + 1);
241
+ if (format === 'zip') {
242
+ return execFileSync('unzip', ['-p', archivePath, entry], { encoding: null, maxBuffer, stdio: ['ignore', 'pipe', 'pipe'] });
243
+ }
244
+ if (format === 'tar') {
245
+ return execFileSync('tar', ['-xOf', archivePath, entry], { encoding: null, maxBuffer, stdio: ['ignore', 'pipe', 'pipe'] });
246
+ }
247
+ return execFileSync('gzip', ['-dc', archivePath], { encoding: null, maxBuffer, stdio: ['ignore', 'pipe', 'pipe'] });
248
+ }
249
+
250
+ /**
251
+ * Selectively mine indexable content from an archive into destDir.
252
+ * Large/binary/media members remain represented in the receipt but are not copied.
88
253
  * @param {string} archivePath
89
254
  * @param {string} destDir
255
+ * @param {Partial<typeof DEFAULT_ARCHIVE_POLICY>} [options]
90
256
  */
91
- export function extractArchive(archivePath, destDir) {
257
+ export function extractArchive(archivePath, destDir, options = {}) {
258
+ const policy = { ...DEFAULT_ARCHIVE_POLICY, ...options };
259
+ const inspection = inspectArchive(archivePath, policy);
260
+ fs.rmSync(destDir, { recursive: true, force: true });
92
261
  fs.mkdirSync(destDir, { recursive: true });
93
- const lower = archivePath.toLowerCase();
94
- if (lower.endsWith('.zip')) {
95
- if (!hasBin('unzip')) throw new Error('unzip_unavailable: install unzip to extract .zip materials');
96
- execFileSync('unzip', ['-q', '-o', archivePath, '-d', destDir], { stdio: ['ignore', 'pipe', 'pipe'] });
97
- return { tool: 'unzip', dest: destDir };
98
- }
99
- if (lower.endsWith('.tar') || lower.endsWith('.tar.gz') || lower.endsWith('.tgz')
100
- || lower.endsWith('.tar.bz2') || lower.endsWith('.tbz2') || lower.endsWith('.gz')) {
101
- if (!hasBin('tar')) throw new Error('tar_unavailable: install tar to extract archive materials');
102
- execFileSync('tar', ['-xf', archivePath, '-C', destDir], { stdio: ['ignore', 'pipe', 'pipe'] });
103
- return { tool: 'tar', dest: destDir };
262
+
263
+ const selected = inspection.entries.filter((entry) => entry.selected);
264
+ const mined = [];
265
+ const skipped = inspection.entries.filter((entry) => !entry.selected).map((entry) => ({
266
+ path: entry.path, kind: entry.kind, bytes: entry.bytes, reason: entry.reason,
267
+ }));
268
+ let totalBytes = 0;
269
+
270
+ for (const entry of selected) {
271
+ if (mined.length >= policy.maxFiles) {
272
+ skipped.push({ path: entry.path, kind: entry.kind, bytes: entry.bytes, reason: 'file_limit' });
273
+ continue;
274
+ }
275
+ if (Number.isFinite(entry.bytes) && totalBytes + entry.bytes > policy.maxTotalBytes) {
276
+ skipped.push({ path: entry.path, kind: entry.kind, bytes: entry.bytes, reason: 'total_byte_limit' });
277
+ continue;
278
+ }
279
+
280
+ let payload;
281
+ try {
282
+ payload = readArchiveEntry(inspection.archive, inspection.format, entry.path, policy.maxEntryBytes);
283
+ } catch (cause) {
284
+ skipped.push({ path: entry.path, kind: entry.kind, bytes: entry.bytes, reason: 'extract_error', error: String(cause?.message || cause).slice(0, 240) });
285
+ continue;
286
+ }
287
+ if (payload.length > policy.maxEntryBytes || totalBytes + payload.length > policy.maxTotalBytes) {
288
+ skipped.push({ path: entry.path, kind: entry.kind, bytes: payload.length, reason: payload.length > policy.maxEntryBytes ? 'entry_too_large' : 'total_byte_limit' });
289
+ continue;
290
+ }
291
+
292
+ const normalized = entry.path.replace(/\\/g, '/').replace(/^\.\//, '');
293
+ const output = path.resolve(destDir, ...normalized.split('/'));
294
+ const root = path.resolve(destDir);
295
+ if (!(output === root || output.startsWith(`${root}${path.sep}`))) {
296
+ throw new Error(`archive_unsafe_output:${entry.path}`);
297
+ }
298
+ fs.mkdirSync(path.dirname(output), { recursive: true });
299
+ fs.writeFileSync(output, payload);
300
+ totalBytes += payload.length;
301
+ mined.push({ path: entry.path, kind: entry.kind, bytes: payload.length });
104
302
  }
105
- throw new Error(`unsupported_archive:${path.basename(archivePath)}`);
303
+
304
+ return {
305
+ schema: 'agentsam.archive.mine.v1',
306
+ format: inspection.format,
307
+ dest: destDir,
308
+ entries_total: inspection.entries_total,
309
+ mined_files: mined.length,
310
+ mined_bytes: totalBytes,
311
+ skipped_files: skipped.length,
312
+ by_kind: inspection.by_kind,
313
+ policy,
314
+ mined,
315
+ skipped: skipped.slice(0, 200),
316
+ skipped_truncated: skipped.length > 200,
317
+ };
106
318
  }
107
319
 
108
320
  /**
109
321
  * Stage pasted/dropped materials under `.agentsam/ingest/<id>/`.
110
- * Archives are extracted; files/dirs are copied or linked by relative include paths.
322
+ * Archives are inspected + selectively mined; files/dirs are copied or linked by relative include paths.
111
323
  *
112
- * @param {{ root: string, materials: string[], ingestId?: string }} opts
324
+ * @param {{ root: string, materials: string[], ingestId?: string, archivePolicy?: object }} opts
113
325
  */
114
326
  export function stageMaterials(opts) {
115
327
  const root = path.resolve(opts.root);
@@ -117,11 +329,8 @@ export function stageMaterials(opts) {
117
329
  const stageRoot = path.join(root, '.agentsam', 'ingest', ingestId);
118
330
  fs.mkdirSync(stageRoot, { recursive: true });
119
331
 
120
- /** @type {object[]} */
121
332
  const items = [];
122
- /** @type {string[]} */
123
333
  const include = [];
124
- /** @type {object[]} */
125
334
  const assets = [];
126
335
 
127
336
  for (const raw of opts.materials) {
@@ -133,11 +342,10 @@ export function stageMaterials(opts) {
133
342
 
134
343
  if (classified.kind === 'archive') {
135
344
  const dest = path.join(stageRoot, 'archives', path.basename(classified.path, classified.ext) || 'archive');
136
- fs.mkdirSync(dest, { recursive: true });
137
- const extracted = extractArchive(classified.path, dest);
345
+ const extracted = extractArchive(classified.path, dest, opts.archivePolicy || {});
138
346
  const rel = path.relative(root, dest);
139
- include.push(rel);
140
- items.push({ ...classified, status: 'extracted', stage: dest, extract: extracted });
347
+ if (extracted.mined_files > 0) include.push(rel);
348
+ items.push({ ...classified, status: 'mined', stage: dest, extract: extracted });
141
349
  continue;
142
350
  }
143
351
 
@@ -158,7 +366,6 @@ export function stageMaterials(opts) {
158
366
  continue;
159
367
  }
160
368
 
161
- // Single file
162
369
  const destDir = path.join(stageRoot, classified.kind === 'image' ? 'images'
163
370
  : classified.kind === 'model3d' ? 'models3d'
164
371
  : classified.kind === 'document' ? 'documents'
@@ -180,7 +387,7 @@ export function stageMaterials(opts) {
180
387
 
181
388
  const uniqueInclude = [...new Set(include.map((p) => p.replace(/\\/g, '/').replace(/\/$/, '') || '.'))].sort();
182
389
  const manifest = {
183
- schema: 'agentsam.ingest.materials.v1',
390
+ schema: 'agentsam.ingest.materials.v2',
184
391
  ingest_id: ingestId,
185
392
  staged_at: new Date().toISOString(),
186
393
  stage_root: path.relative(root, stageRoot),
@@ -77,6 +77,7 @@ export function encodeErrorDetail(value) {
77
77
  detail.setDomain(envelope.domain);
78
78
  if (envelope.tool) detail.setTool(envelope.tool);
79
79
  if (envelope.stage) detail.setStage(envelope.stage);
80
+ if (envelope.native_stage) detail.setNativeStage(envelope.native_stage);
80
81
  detail.setRemediationAction(envelope.remediation.action);
81
82
  if (envelope.remediation.message) detail.setRemediationMessage(envelope.remediation.message);
82
83
  detail.setFingerprint(envelope.fingerprint);
@@ -115,6 +116,7 @@ export function decodeErrorDetail(detail) {
115
116
  domain: detail.hasDomain?.() ? detail.getDomain() : 'runtime',
116
117
  tool: detail.hasTool?.() ? detail.getTool() : null,
117
118
  stage: detail.hasStage?.() ? detail.getStage() : null,
119
+ native_stage: detail.hasNativeStage?.() ? detail.getNativeStage() : null,
118
120
  remediation: {
119
121
  action: detail.hasRemediationAction?.() ? detail.getRemediationAction() : undefined,
120
122
  message: detail.hasRemediationMessage?.() ? detail.getRemediationMessage() : null,
@@ -380,14 +380,16 @@ export function renderRuntimePlan(plan) {
380
380
  lines.push(` missing tools: ${c.constraints.missing_tools.join(', ')}`);
381
381
  }
382
382
  } else if (c.goap.action_ids.length) {
383
- lines.push(` plan: ${c.goap.action_ids.join(' → ')}`);
383
+ lines.push(` target plan: ${c.goap.action_ids.join(' → ')}`);
384
384
  }
385
385
  }
386
386
  lines.push('');
387
387
  if (plan.recommended) {
388
388
  lines.push(` Recommended ${plan.recommended.profile.label} (${plan.recommended.profile.id})`);
389
389
  lines.push(` Next agentsam setup runtime --profile ${plan.recommended.profile.id} --yes`);
390
- lines.push(` agentsam runtime install`);
390
+ lines.push(plan.facts.agentsamd.installed
391
+ ? ' agentsam runtime status'
392
+ : ' agentsam runtime install');
391
393
  }
392
394
  lines.push('');
393
395
  return `${lines.join('\n')}\n`;
@@ -12,6 +12,7 @@ export const SLASH_COMMANDS = [
12
12
  { cmd: '/context', description: 'Show model context economics; add repo for Git bridge context', lane: 'context' },
13
13
  { cmd: '/status', description: 'Honest project awareness: account, models, terminal, live deploy', lane: 'local' },
14
14
  { cmd: '/models', description: 'Probe providers and provider-verified known models', lane: 'model' },
15
+ { cmd: '/hooks', description: 'Inspect lifecycle hook sources, order, and execution receipts', lane: 'agent' },
15
16
  { cmd: '/providers', description: 'Configure and verify machine provider credentials', lane: 'model' },
16
17
  { cmd: '/credentials', description: 'Audit credentials, drift, and remediation (never prints secrets)', lane: 'identity' },
17
18
  { cmd: '/cheat-sheet', description: 'Organized Agent Sam command groups', lane: 'local' },
@@ -4,6 +4,7 @@ import { createLocalSqliteDatabase } from './sqlite.js';
4
4
  import { applyRuntimeMigrations } from './migrations.js';
5
5
  import { findCliProjectRoot } from '../lib/cli-preferences.js';
6
6
  import { getLocalDatabasePath, tryReadProjectConfig } from '../lib/project-config.js';
7
+ import { createHookStore } from '../../packages/agentsam-hooks/src/store.js';
7
8
 
8
9
  function clean(value) { return value == null ? '' : String(value).trim(); }
9
10
  function hash(value) { return createHash('sha256').update(String(value || '')).digest('hex'); }
@@ -187,3 +188,48 @@ export async function pruneExpiredRuntimeState(value = {}) {
187
188
  return { digests: Number(digests.changes || 0), compactions: Number(compactions.changes || 0), approvals_expired: Number(approvals.changes || 0) };
188
189
  });
189
190
  }
191
+
192
+ function hookStore(db, value = {}) {
193
+ return createHookStore(db, { ownerId: clean(value.owner_id || value.account_id) || 'local' });
194
+ }
195
+
196
+ export async function listRuntimeHooks(value = {}) {
197
+ return withStore(value.projectRoot || value.cwd, (db) => hookStore(db, value).listHooks({
198
+ activeOnly: value.activeOnly,
199
+ event_type: value.event_type,
200
+ context: value.context,
201
+ }));
202
+ }
203
+
204
+ export async function upsertRuntimeHook(value = {}) {
205
+ return withStore(value.projectRoot || value.cwd, (db) => hookStore(db, value).upsertHook(value));
206
+ }
207
+
208
+ export async function setRuntimeHookActive(value = {}) {
209
+ return withStore(value.projectRoot || value.cwd, (db) => hookStore(db, value).setHookActive(value.hook_id || value.hook_key, value.active));
210
+ }
211
+
212
+ export async function removeRuntimeHook(value = {}) {
213
+ return withStore(value.projectRoot || value.cwd, (db) => hookStore(db, value).removeHook(value.hook_id || value.hook_key));
214
+ }
215
+
216
+ export async function recordRuntimeHookReceipt(value = {}) {
217
+ if (!value.receipt) return null;
218
+ return withStore(value.projectRoot || value.cwd, (db) => hookStore(db, value).recordExecution(value.receipt, {
219
+ source_kind: value.source_kind,
220
+ hook_id: value.hook_id,
221
+ invocation_id: value.invocation_id,
222
+ agent_run_id: value.agent_run_id,
223
+ session_id: value.session_id,
224
+ conversation_id: value.conversation_id,
225
+ context: value.context,
226
+ }));
227
+ }
228
+
229
+ export async function listRuntimeHookExecutions(value = {}) {
230
+ return withStore(value.projectRoot || value.cwd, (db) => hookStore(db, value).listExecutions({
231
+ hook_key: value.hook_key,
232
+ status: value.status,
233
+ limit: value.limit,
234
+ }));
235
+ }
@@ -0,0 +1,37 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+
6
+ export const ASSET_MANIFEST_SCHEMA = 'agentsam.local-model-assets.v1';
7
+ export function agentsamHome(env = process.env) {
8
+ const configured = String(env.AGENTSAM_HOME || '').trim();
9
+ return configured ? path.resolve(configured.replace(/^~(?=\/|$)/, os.homedir())) : path.join(os.homedir(), '.agentsam');
10
+ }
11
+ export function assetManifestPath(env = process.env) { return path.join(agentsamHome(env), 'models', 'manifest.v1.json'); }
12
+ function safeStat(file) { try { return fs.statSync(file); } catch { return null; } }
13
+ function fileId(stat) { return stat ? `${stat.dev}:${stat.ino}` : null; }
14
+ export function readAssetManifest(env = process.env) {
15
+ const file = assetManifestPath(env);
16
+ if (!fs.existsSync(file)) return { schema_version: ASSET_MANIFEST_SCHEMA, generated_at: null, assets: [] };
17
+ try { const parsed = JSON.parse(fs.readFileSync(file, 'utf8')); return parsed?.schema_version === ASSET_MANIFEST_SCHEMA ? parsed : { schema_version: ASSET_MANIFEST_SCHEMA, generated_at: null, assets: [] }; } catch { return { schema_version: ASSET_MANIFEST_SCHEMA, generated_at: null, assets: [] }; }
18
+ }
19
+ function writeManifest(manifest, env = process.env) {
20
+ const file = assetManifestPath(env); fs.mkdirSync(path.dirname(file), { recursive: true });
21
+ const temp = `${file}.${process.pid}.tmp`; fs.writeFileSync(temp, JSON.stringify(manifest, null, 2) + '\n', { mode: 0o600 }); fs.renameSync(temp, file); return file;
22
+ }
23
+ export function adoptAsset(file, metadata = {}, options = {}) {
24
+ const absolute = path.resolve(file); const stat = safeStat(absolute);
25
+ if (!stat || !stat.isFile()) throw new Error(`model asset is not a regular file: ${absolute}`);
26
+ const env = options.env || process.env; const manifest = readAssetManifest(env);
27
+ const asset = { id: metadata.id || crypto.createHash('sha256').update(`${absolute}:${stat.size}:${stat.mtimeMs}`).digest('hex').slice(0, 16), engine_id: metadata.engine_id || null, model: metadata.model || path.basename(absolute), format: metadata.format || path.extname(absolute).slice(1) || 'unknown', canonical_path: absolute, reference: 'external', size_bytes: stat.size, file_id: fileId(stat), digest: metadata.digest || null, seen_at: new Date().toISOString() };
28
+ const assets = manifest.assets.filter((row) => row.canonical_path !== absolute); assets.push(asset);
29
+ const next = { ...manifest, schema_version: ASSET_MANIFEST_SCHEMA, generated_at: new Date().toISOString(), assets }; const manifestPath = writeManifest(next, env);
30
+ return { asset, manifest_path: manifestPath, copied: false };
31
+ }
32
+ export function auditAssets(options = {}) {
33
+ const env = options.env || process.env; const manifest = readAssetManifest(env);
34
+ const assets = manifest.assets.map((asset) => { const stat = safeStat(asset.canonical_path); return { ...asset, exists: Boolean(stat), current_size_bytes: stat?.size || null, current_file_id: fileId(stat), stale: !stat || stat.size !== asset.size_bytes }; });
35
+ const byFile = new Map(); for (const asset of assets) if (asset.file_id) byFile.set(asset.file_id, [...(byFile.get(asset.file_id) || []), asset.id]);
36
+ return { schema_version: ASSET_MANIFEST_SCHEMA, generated_at: new Date().toISOString(), root: path.join(agentsamHome(env), 'models'), assets, duplicates: [...byFile.entries()].filter(([, ids]) => ids.length > 1).map(([file_id, ids]) => ({ file_id, asset_ids: ids })) };
37
+ }