@inneranimalmedia/agentsam-sdk 2.6.3 → 2.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/AGENTSAM.md +6 -0
  2. package/README.md +16 -1
  3. package/bin/agentsam +16 -1
  4. package/docs/BRAND_INTELLIGENCE.md +1 -1
  5. package/docs/architecture/AGENTSAM_DISTRIBUTION_OWNERSHIP.md +176 -0
  6. package/docs/architecture/AGENTSAM_GO_RUNTIME.md +282 -0
  7. package/docs/architecture/CODEBASEINDEX_GUIDED_PATH.md +202 -0
  8. package/docs/architecture/FS_E2E_CLOSURE_RECEIPT.md +59 -0
  9. package/docs/architecture/LOCAL_FS_AUTHORITY.md +38 -0
  10. package/docs/architecture/LOCAL_STUDIO_CLOUDFLARE_OAUTH.md +28 -0
  11. package/docs/architecture/PLAN_CLI_AND_LOCAL_STUDIO_DESKTOP.md +96 -0
  12. package/docs/architecture/SAM_ACTIVITY_RECOVERY_RECEIPT.md +39 -0
  13. package/docs/architecture/SAM_DECISION_WORK_RECEIPT.md +54 -0
  14. package/docs/architecture/SAM_KERNEL.md +181 -0
  15. package/docs/architecture/SAM_MACHINE_NORMALIZATION_PRECOMMIT_REPORT.md +208 -0
  16. package/docs/architecture/SLASH_SKILLS_PORTABLE.md +116 -0
  17. package/docs/architecture/fs-e2e-receipt.latest.json +42 -0
  18. package/docs/architecture/previews/codebaseindex-guided-path/CODEBASEINDEX_GUIDED_PATH.md +202 -0
  19. package/docs/architecture/previews/codebaseindex-guided-path/index.html +321 -0
  20. package/migrations/d1/0010_portable_tickets_memory.sql +140 -0
  21. package/migrations/d1/0011_agentsam_skill_v2.sql +185 -0
  22. package/migrations/d1/0011b_agentsam_skill_v2_cutover.sql +22 -0
  23. package/migrations/d1/0011c_agentsam_skill_v2_backfill.sql +76 -0
  24. package/migrations/d1/0011d_agentsam_skill_v2_retrieval_revisions.sql +50 -0
  25. package/migrations/d1/0012_agentsam_tools_required_seed.sql +67 -0
  26. package/migrations/d1/0013_identity_oauth_states.sql +15 -0
  27. package/migrations/d1/0014_auth_event_log.sql +20 -0
  28. package/migrations/d1/0015_identity_oauth_state_app_id.sql +3 -0
  29. package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +16 -0
  30. package/migrations/sqlite/agentsam_skill_retrieval.portable.sql +42 -0
  31. package/package.json +19 -4
  32. package/packages/agentsam-contracts/src/errors.ts +16 -0
  33. package/packages/agentsam-errors/src/envelope.js +53 -0
  34. package/packages/agentsam-errors/src/index.js +1 -0
  35. package/packages/agentsam-errors/src/recovery.js +301 -0
  36. package/packages/agentsam-knowledge/src/providers/index.js +18 -6
  37. package/packages/connectors/cloudflare/src/routes.js +9 -0
  38. package/packages/connectors/cloudflare/tests/connector.test.mjs +26 -1
  39. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
  40. package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +11 -2
  41. package/packages/identity/docs/PORTABLE_IDENTITY_ARCHITECTURE.md +50 -0
  42. package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +31 -0
  43. package/packages/identity/migrations/sqlite/001_identity_core.sql +99 -0
  44. package/packages/identity/migrations/sqlite/002_identity_oauth_client.sql +38 -0
  45. package/packages/identity/migrations/sqlite/003_identity_oauth_server.sql +64 -0
  46. package/packages/identity/package.json +2 -2
  47. package/packages/identity/src/adapters/cloudflare-d1/index.js +122 -22
  48. package/packages/identity/src/adapters/sqlite/index.js +319 -0
  49. package/packages/identity/src/app/verify-app.js +95 -0
  50. package/packages/identity/src/contracts/identity-store.js +115 -0
  51. package/packages/identity/src/contracts/route-ids.js +30 -0
  52. package/packages/identity/src/contracts/route-projection.js +218 -0
  53. package/packages/identity/src/contracts/routes.js +11 -0
  54. package/packages/identity/src/core/browser-paths.js +4 -5
  55. package/packages/identity/src/core/constants.js +13 -8
  56. package/packages/identity/src/core/session-policy.js +32 -0
  57. package/packages/identity/src/frontend/auth-portal/pages/login.html +10 -10
  58. package/packages/identity/src/frontend/auth-portal/pages/reset.html +3 -3
  59. package/packages/identity/src/frontend/auth-portal/pages/signup.html +3 -3
  60. package/packages/identity/src/frontend/auth-portal/preview/dashboard-stub.html +1 -1
  61. package/packages/identity/src/index.js +25 -0
  62. package/packages/identity/src/oauth/README.md +10 -4
  63. package/packages/identity/src/oauth/credentials.js +20 -13
  64. package/packages/identity/src/oauth/finalize-inbound.js +1 -1
  65. package/packages/identity/src/oauth/iam-platform.js +8 -7
  66. package/packages/identity/src/oauth/redirect-paths.js +27 -36
  67. package/packages/identity/src/server/identity-service.js +30 -13
  68. package/packages/identity/src/server/mount-policy.js +30 -0
  69. package/packages/identity/src/server/post-auth.js +79 -0
  70. package/packages/identity/src/server/worker-router.js +104 -72
  71. package/packages/identity/tests/finalize-inbound-oauth.test.mjs +6 -6
  72. package/packages/identity/tests/iam-provider.test.mjs +1 -1
  73. package/packages/identity/tests/identity-service.test.mjs +36 -5
  74. package/packages/identity/tests/oauth-credentials.test.mjs +3 -1
  75. package/packages/identity/tests/portable-identity-architecture.test.mjs +157 -0
  76. package/packages/identity/tests/session-routes-policy.test.mjs +21 -0
  77. package/packages/theme-church-site/package.json +2 -1
  78. package/packages/theme-church-site/src/index.js +1 -0
  79. package/packages/theme-companions-site/package.json +2 -1
  80. package/packages/theme-companions-site/src/index.js +1 -0
  81. package/packages/theme-floors-site/package.json +2 -1
  82. package/packages/theme-floors-site/src/index.js +1 -0
  83. package/packages/theme-fuelnfree-site/package.json +2 -1
  84. package/packages/theme-fuelnfree-site/src/index.js +1 -0
  85. package/packages/theme-handyman-site/package.json +2 -1
  86. package/packages/theme-handyman-site/src/index.js +1 -0
  87. package/packages/theme-insurance-site/package.json +2 -1
  88. package/packages/theme-insurance-site/src/index.js +1 -0
  89. package/packages/theme-shinshu-site/package.json +2 -1
  90. package/packages/theme-shinshu-site/src/index.js +1 -0
  91. package/protocol/apps/agentsam.app.v1.schema.json +51 -0
  92. package/protocol/brand/brandpack.v1.schema.json +43 -0
  93. package/protocol/credentials/issue.v1.schema.json +38 -0
  94. package/protocol/database/connection.v1.schema.json +41 -0
  95. package/protocol/embeddings/embedding-profile.v1.schema.json +20 -0
  96. package/protocol/errors/error-envelope.schema.json +135 -1
  97. package/protocol/errors/recovery.v1.schema.json +50 -0
  98. package/protocol/runtime/workspace-fs.v1.schema.json +71 -0
  99. package/protocol/sam/activity.v1.schema.json +48 -0
  100. package/protocol/sam/answer.v1.schema.json +35 -0
  101. package/protocol/sam/calibration.v1.schema.json +21 -0
  102. package/protocol/sam/decision-receipt.v1.schema.json +29 -0
  103. package/protocol/sam/evaluation.v1.schema.json +19 -0
  104. package/protocol/sam/operation.schema.json +66 -0
  105. package/protocol/sam/outcome.v1.schema.json +36 -0
  106. package/protocol/sam/question.v1.schema.json +26 -0
  107. package/protocol/sam/registry.seed.json +153 -0
  108. package/protocol/sam/result.schema.json +53 -0
  109. package/protocol/sam/state.v1.schema.json +23 -0
  110. package/protocol/skills/agentsam.interaction.v1.schema.json +50 -0
  111. package/protocol/skills/agentsam.skill.v1.schema.json +57 -0
  112. package/protocol/ui/icon-registry.mjs +226 -0
  113. package/protocol/ui/icon.v1.schema.json +51 -0
  114. package/skills/README.md +22 -9
  115. package/skills/agentsam-codebaseindex/SKILL.md +225 -0
  116. package/skills/catalog.json +14 -0
  117. package/src/cli/command-catalog.js +130 -0
  118. package/src/cli/dispatch.js +48 -0
  119. package/src/cli.js +43 -1
  120. package/src/commands/api-key.js +244 -0
  121. package/src/commands/app.js +60 -28
  122. package/src/commands/brand.js +17 -19
  123. package/src/commands/codebaseindex.js +688 -0
  124. package/src/commands/env.js +152 -25
  125. package/src/commands/go.js +366 -53
  126. package/src/commands/interaction-clack.js +115 -0
  127. package/src/commands/models.js +1 -1
  128. package/src/commands/providers.js +62 -14
  129. package/src/commands/shell.js +43 -2
  130. package/src/commands/skill.js +248 -0
  131. package/src/commands/skills.js +1 -1
  132. package/src/commands/start-local.js +4 -0
  133. package/src/commands/whoami.js +90 -18
  134. package/src/go/build.js +229 -39
  135. package/src/go/cloudflare.js +506 -105
  136. package/src/go/container.js +120 -0
  137. package/src/go/contract.js +9 -4
  138. package/src/go/discover.js +149 -33
  139. package/src/go/index.js +15 -3
  140. package/src/go/native-probe-runner.mjs +119 -0
  141. package/src/go/{registry.js → official-registry.js} +64 -7
  142. package/src/go/official-release.js +10 -0
  143. package/src/go/receipts.js +77 -10
  144. package/src/go/verify.js +9 -2
  145. package/src/index.js +27 -0
  146. package/src/indexing/ingest/discover-models.js +298 -0
  147. package/src/indexing/ingest/inventory.js +243 -0
  148. package/src/indexing/ingest/job-graph.js +181 -0
  149. package/src/indexing/ingest/materials.js +210 -0
  150. package/src/lib/provider-credentials.js +63 -21
  151. package/src/lib/slash-commands.js +1 -0
  152. package/src/local-fs/capability.js +121 -0
  153. package/src/local-fs/freshness.js +75 -0
  154. package/src/local-fs/index.js +385 -0
  155. package/src/local-fs/paths.js +100 -0
  156. package/src/local-pty/server.js +295 -19
  157. package/src/mcp/client.js +2 -2
  158. package/src/models/ai-access-onboarding.js +112 -0
  159. package/src/models/discovery.js +10 -2
  160. package/src/models/inventory-core.js +9 -1
  161. package/src/sam/activity/index.js +183 -0
  162. package/src/sam/client.js +252 -0
  163. package/src/sam/decision/calibration.js +109 -0
  164. package/src/sam/decision/confidence.js +126 -0
  165. package/src/sam/decision/evaluate.js +157 -0
  166. package/src/sam/decision/evaluators/deterministic.js +341 -0
  167. package/src/sam/decision/evaluators/heuristic.js +61 -0
  168. package/src/sam/decision/evaluators/select.js +50 -0
  169. package/src/sam/decision/evaluators/semantic.js +149 -0
  170. package/src/sam/decision/hierarchical.js +61 -0
  171. package/src/sam/decision/index.js +53 -0
  172. package/src/sam/decision/policy.js +86 -0
  173. package/src/sam/decision/questions.js +120 -0
  174. package/src/sam/decision/receipt.js +148 -0
  175. package/src/sam/decision/state.js +117 -0
  176. package/src/sam/decision/types.js +22 -0
  177. package/src/sam/decision/validate.js +200 -0
  178. package/src/sam/define.js +51 -0
  179. package/src/sam/index.js +65 -0
  180. package/src/sam/operations/brand-scan.js +62 -0
  181. package/src/sam/operations/cad-blender-inspect.js +36 -0
  182. package/src/sam/operations/codebaseindex-ingest.js +49 -0
  183. package/src/sam/operations/decision-evaluate.js +59 -0
  184. package/src/sam/operations/planning-astar.js +77 -0
  185. package/src/sam/operations/planning-goap.js +60 -0
  186. package/src/sam/operations/repository-inspect.js +72 -0
  187. package/src/sam/operations/security-scan.js +33 -0
  188. package/src/sam/operations/terminal-exec.js +29 -0
  189. package/src/sam/planning/astar.js +311 -0
  190. package/src/sam/planning/goap.js +177 -0
  191. package/src/sam/planning/index.js +21 -0
  192. package/src/sam/planning/state.js +84 -0
  193. package/src/sam/registry.js +48 -0
  194. package/src/sam/result.js +77 -0
  195. package/src/sam/seed.js +44 -0
  196. package/src/sam/types.js +91 -0
  197. package/src/skills/catalog.js +64 -0
  198. package/src/skills/content-resolver.js +124 -0
  199. package/src/skills/hosted-store.js +37 -0
  200. package/src/skills/index.js +29 -64
  201. package/src/skills/interaction.js +102 -0
  202. package/src/skills/local-store.js +228 -0
  203. package/src/skills/manifest.js +104 -0
  204. package/src/skills/metrics.js +31 -0
  205. package/src/skills/registry.js +184 -0
  206. package/src/skills/runtime.js +287 -0
  207. package/src/skills/slash.js +44 -0
  208. package/src/ui/cli/help.js +94 -101
  209. package/test/cli/api-key-env-whoami.test.mjs +129 -0
  210. package/test/cli/codebaseindex-plan-ux.test.mjs +37 -0
  211. package/test/cli/go.test.mjs +62 -5
  212. package/test/cli/skill-npm-and-env.test.mjs +52 -0
  213. package/test/cli/wireframes-go-registry.test.mjs +87 -2
  214. package/test/go/build-source-identity.test.mjs +31 -0
  215. package/test/go/cloudflare-probe.test.mjs +274 -10
  216. package/test/go/distribution.test.mjs +28 -0
  217. package/test/integration/ai-access-onboarding.test.mjs +47 -0
  218. package/test/integration/cli-help.test.mjs +1 -1
  219. package/test/integration/cms-site-tenancy-contract.test.mjs +6 -6
  220. package/test/integration/codebaseindex-ingest.test.mjs +166 -0
  221. package/test/integration/icon-registry.test.mjs +67 -0
  222. package/test/integration/ingest-discover-models.test.mjs +30 -0
  223. package/test/integration/install-script.test.mjs +12 -9
  224. package/test/integration/local-fs.test.mjs +113 -0
  225. package/test/integration/provider-env-cli.test.mjs +2 -1
  226. package/test/integration/sam-activity-recovery.test.mjs +147 -0
  227. package/test/integration/sam-decision.test.mjs +584 -0
  228. package/test/integration/sam-kernel.test.mjs +99 -0
  229. package/test/integration/sam-planning-astar.test.mjs +279 -0
  230. package/test/integration/skill-runtime.test.mjs +240 -0
  231. package/test/integration/studio-fs-pty-e2e.test.mjs +294 -0
  232. package/test/models.test.mjs +14 -7
  233. package/test/shell.test.mjs +4 -4
@@ -0,0 +1,77 @@
1
+ import { defineSamOperation } from '../define.js';
2
+ import { planAStar, ASTAR_ENGINE } from '../planning/astar.js';
3
+ import { hashJson } from '../result.js';
4
+ import { stableStateKey } from '../planning/state.js';
5
+
6
+ /**
7
+ * Pure A* search — domain-independent machine primitive.
8
+ * Input supplies expand/isGoal; handler does not call models or D1.
9
+ */
10
+ export const planningAstarOp = defineSamOperation({
11
+ id: 'planning.astar',
12
+ version: 1,
13
+ module: 'planning',
14
+ action: 'astar',
15
+ summary: 'Run domain-independent A* search over an explicit expand graph.',
16
+ purpose:
17
+ 'Find a lowest-cost path through an explicit state graph using f(n)=g(n)+h(n).',
18
+ outcome:
19
+ 'Return a typed plan with cost, expansion stats, and deterministic receipt evidence.',
20
+ description:
21
+ 'Systematic Autonomous Machinery planner. Default heuristic is 0 (Dijkstra). No model spend.',
22
+ skill: { id: 'agentsam-app-fundamentals', help: true },
23
+ execution: {
24
+ lanes: ['local', 'remote', 'sandbox'],
25
+ model: 'never',
26
+ embedding: 'never',
27
+ network: 'none',
28
+ sideEffects: 'none',
29
+ provider_spend: 'none',
30
+ },
31
+ risk: 'read_only',
32
+ capabilities: ['planning.astar'],
33
+ artifacts: ['planning.plan', 'planning.receipt'],
34
+ cli: { command: [] },
35
+ docs: { section: 'Planning', examples: ['planning-astar-basic'] },
36
+ status: 'stable',
37
+ async handler(input = {}, ctx = {}) {
38
+ const {
39
+ initialState,
40
+ isGoal,
41
+ expand,
42
+ heuristic,
43
+ stateKey,
44
+ maxExpansions,
45
+ } = input || {};
46
+
47
+ if (typeof isGoal !== 'function' || typeof expand !== 'function') {
48
+ const err = new Error('planning.astar requires isGoal and expand functions in input');
49
+ err.code = 'invalid_input';
50
+ throw err;
51
+ }
52
+
53
+ const result = planAStar({
54
+ initialState,
55
+ isGoal,
56
+ expand,
57
+ heuristic: typeof heuristic === 'function' ? heuristic : undefined,
58
+ stateKey: typeof stateKey === 'function' ? stateKey : stableStateKey,
59
+ maxExpansions,
60
+ signal: ctx.signal,
61
+ });
62
+
63
+ return {
64
+ ...result,
65
+ engine: ASTAR_ENGINE,
66
+ evidence: {
67
+ initial_state_hash: hashJson(initialState),
68
+ selected_actions: result.plan.map((step) => step.action),
69
+ total_cost: result.total_cost,
70
+ expanded_states: result.expanded_states,
71
+ max_expansions: maxExpansions ?? null,
72
+ engine: ASTAR_ENGINE,
73
+ deterministic: true,
74
+ },
75
+ };
76
+ },
77
+ });
@@ -0,0 +1,60 @@
1
+ import { defineSamOperation } from '../define.js';
2
+ import { planGoap, GOAP_ENGINE } from '../planning/goap.js';
3
+ import { hashJson } from '../result.js';
4
+
5
+ /**
6
+ * GOAP planning via A* — precondition/effect actions over flat world state.
7
+ */
8
+ export const planningGoapOp = defineSamOperation({
9
+ id: 'planning.goap',
10
+ version: 1,
11
+ module: 'planning',
12
+ action: 'goap',
13
+ summary: 'Plan a GOAP action sequence with A* (sam-goap-astar-v1).',
14
+ purpose:
15
+ 'Select a lowest-cost sequence of precondition/effect actions that satisfies a goal.',
16
+ outcome:
17
+ 'Return action ids, total cost, expansion stats, and deterministic receipt evidence.',
18
+ description:
19
+ 'Thin GOAP adapter over planning.astar. Replaces greedy cheapest-applicable loops.',
20
+ skill: { id: 'agentsam-app-fundamentals', help: true },
21
+ execution: {
22
+ lanes: ['local', 'remote', 'sandbox'],
23
+ model: 'never',
24
+ embedding: 'never',
25
+ network: 'none',
26
+ sideEffects: 'none',
27
+ provider_spend: 'none',
28
+ },
29
+ risk: 'read_only',
30
+ capabilities: ['planning.goap', 'planning.astar'],
31
+ artifacts: ['planning.goap.plan', 'planning.receipt'],
32
+ cli: { command: [['plan', 'brand', '--goap']] },
33
+ docs: { section: 'Planning', examples: ['planning-goap-basic'] },
34
+ status: 'stable',
35
+ async handler(input = {}, ctx = {}) {
36
+ const { initialState, goal, actions, heuristic, maxExpansions } = input || {};
37
+ const result = planGoap({
38
+ initialState,
39
+ goal,
40
+ actions,
41
+ heuristic: typeof heuristic === 'function' ? heuristic : undefined,
42
+ maxExpansions,
43
+ signal: ctx.signal,
44
+ });
45
+
46
+ return {
47
+ ...result,
48
+ evidence: {
49
+ initial_state_hash: hashJson(initialState),
50
+ goal_hash: hashJson(goal),
51
+ selected_actions: result.action_ids || result.plan || [],
52
+ total_cost: result.total_cost ?? result.cost,
53
+ expanded_states: result.expanded_states,
54
+ max_expansions: maxExpansions ?? null,
55
+ engine: GOAP_ENGINE,
56
+ deterministic: true,
57
+ },
58
+ };
59
+ },
60
+ });
@@ -0,0 +1,72 @@
1
+ import { defineSamOperation } from '../define.js';
2
+ import { repositorySnapshot } from '../../capabilities/index.js';
3
+
4
+ /**
5
+ * Ambient repository authority — not merely “look at files.”
6
+ */
7
+ export const repositoryInspect = defineSamOperation({
8
+ id: 'repository.inspect',
9
+ version: 1,
10
+ module: 'repository',
11
+ action: 'inspect',
12
+ summary: 'Establish bounded, content-addressed repository snapshot authority.',
13
+ purpose:
14
+ 'Establish a bounded, content-addressed machine understanding of the current repository/environment and expose a reusable repository snapshot authority.',
15
+ outcome:
16
+ 'Return repository identity, revision, merkle/AST evidence, package inventory, systems/layers/facets, and snapshot refs other modules can consume.',
17
+ description:
18
+ 'Resolve cwd/Git root, portable repository identity, revision/branch/remotes; collect merkle + intelligence; expose bounded index/facet/file views; optionally reuse or save canonical snapshots.',
19
+ skill: { id: 'agentsam-app-fundamentals', help: true },
20
+ accepts: ['cwd', 'repository', 'directory', 'snapshot_file'],
21
+ phases: [
22
+ 'resolve_root',
23
+ 'resolve_identity',
24
+ 'resolve_revision',
25
+ 'snapshot_or_scan',
26
+ 'classify',
27
+ 'project_views',
28
+ 'emit_refs',
29
+ ],
30
+ execution: {
31
+ lanes: ['local', 'remote', 'sandbox'],
32
+ model: 'never',
33
+ embedding: 'never',
34
+ network: 'none',
35
+ sideEffects: 'none',
36
+ provider_spend: 'none',
37
+ },
38
+ risk: 'read_only',
39
+ capabilities: ['repository.snapshot', 'repository.ast', 'repository.merkle'],
40
+ artifacts: ['repository.snapshot', 'evidence.refs'],
41
+ cli: { command: [['inspect']] },
42
+ docs: { section: 'Repository', examples: ['repository-inspect-basic'] },
43
+ input_schema: 'protocol/capabilities/repository-snapshot-input.schema.json',
44
+ output_schema: 'protocol/capabilities/repository-snapshot.schema.json',
45
+ status: 'stable',
46
+ async handler(input = {}, ctx = {}) {
47
+ const root = typeof input === 'string'
48
+ ? input
49
+ : (input.root || input.cwd || ctx.cwd || process.cwd());
50
+ const churnDays = Number.isInteger(input.churnDays) ? input.churnDays : 30;
51
+ const snapshot = await repositorySnapshot({ cwd: root, churnDays });
52
+ return {
53
+ summary: snapshot.intelligence?.summary || null,
54
+ repository: snapshot.repository || null,
55
+ revision: snapshot.repository?.revision_sha || null,
56
+ snapshot_id: snapshot.snapshot_id || null,
57
+ snapshot_hash: snapshot.tree?.merkle_root || null,
58
+ merkle_root: snapshot.tree?.merkle_root || null,
59
+ file_count: Array.isArray(snapshot.tree?.paths) ? snapshot.tree.paths.length : null,
60
+ languages: snapshot.intelligence?.languages || null,
61
+ packages: snapshot.packages || null,
62
+ systems: snapshot.intelligence?.top_level || null,
63
+ facets: snapshot.intelligence || null,
64
+ evidence_refs: {
65
+ snapshot_id: snapshot.snapshot_id || null,
66
+ merkle_root: snapshot.tree?.merkle_root || null,
67
+ metadata_root: snapshot.tree?.metadata_root || null,
68
+ },
69
+ snapshot,
70
+ };
71
+ },
72
+ });
@@ -0,0 +1,33 @@
1
+ import { defineSamOperation } from '../define.js';
2
+ import { scanProjectSecurity } from '../../security/scan.js';
3
+
4
+ export const securityScanOp = defineSamOperation({
5
+ id: 'security.scan',
6
+ version: 1,
7
+ module: 'security',
8
+ action: 'scan',
9
+ summary: 'Scan project dependencies and trust boundary (SCA).',
10
+ description: 'Deterministic security scan; network optional for OSV advisory lookup.',
11
+ execution: {
12
+ lanes: ['local', 'remote', 'sandbox'],
13
+ model: 'never',
14
+ network: 'optional',
15
+ sideEffects: 'none',
16
+ },
17
+ risk: 'read_only',
18
+ capabilities: ['security.scan'],
19
+ cli: { command: [['security', 'scan'], ['sca', 'scan']] },
20
+ docs: { section: 'Security' },
21
+ status: 'stable',
22
+ async handler(input = {}, ctx = {}) {
23
+ const root = typeof input === 'string'
24
+ ? input
25
+ : (input.root || input.cwd || input.projectRoot || ctx.cwd || process.cwd());
26
+ return scanProjectSecurity({
27
+ projectRoot: root,
28
+ offline: input.offline === true,
29
+ log: input.log,
30
+ signal: ctx.signal,
31
+ });
32
+ },
33
+ });
@@ -0,0 +1,29 @@
1
+ import { defineSamOperation } from '../define.js';
2
+ import { terminalExec } from '../../capabilities/index.js';
3
+
4
+ export const terminalExecOp = defineSamOperation({
5
+ id: 'terminal.exec',
6
+ version: 1,
7
+ module: 'terminal',
8
+ action: 'exec',
9
+ summary: 'Execute a bounded argv process inside the project root.',
10
+ description: 'Model-callable terminal primitive with cwd sandboxing, timeouts, and redacted diagnostics.',
11
+ execution: {
12
+ lanes: ['local', 'sandbox'],
13
+ model: 'never',
14
+ network: 'optional',
15
+ sideEffects: 'local_write',
16
+ },
17
+ risk: 'write',
18
+ capabilities: ['terminal.exec'],
19
+ cli: { command: [] },
20
+ docs: { section: 'Tools' },
21
+ status: 'stable',
22
+ async handler(input = {}, ctx = {}) {
23
+ const cwd = input.projectRoot || input.cwd || ctx.cwd || process.cwd();
24
+ return terminalExec(
25
+ { ...input, cwd: input.cwd || cwd },
26
+ { cwd, signal: ctx.signal, env: ctx.env },
27
+ );
28
+ },
29
+ });
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Domain-independent A* planner for Systematic Autonomous Machinery.
3
+ *
4
+ * f(n) = g(n) + h(n)
5
+ * Default heuristic is 0 (Dijkstra) — preserve optimality when h is admissible.
6
+ *
7
+ * No model / D1 / repository assumptions.
8
+ */
9
+
10
+ import { stableStateKey } from './state.js';
11
+
12
+ export const ASTAR_ENGINE = 'sam-astar-v1';
13
+
14
+ /** @typedef {'found'|'no_plan'|'budget_exhausted'|'cancelled'|'invalid_input'} AStarStatus */
15
+
16
+ /**
17
+ * @typedef {object} AStarStep
18
+ * @property {unknown} action
19
+ * @property {unknown} [state]
20
+ * @property {number} [cost]
21
+ */
22
+
23
+ /**
24
+ * @typedef {object} AStarResult
25
+ * @property {boolean} ok
26
+ * @property {AStarStatus} status
27
+ * @property {AStarStep[]} plan
28
+ * @property {number} total_cost
29
+ * @property {unknown} [final_state]
30
+ * @property {number} expanded_states
31
+ * @property {number} generated_states
32
+ * @property {number} frontier_peak
33
+ * @property {number} elapsed_ms
34
+ * @property {string} engine
35
+ * @property {string} [error]
36
+ */
37
+
38
+ /**
39
+ * Binary min-heap keyed by f, then g (prefer deeper when f ties), then order, then stateKey.
40
+ * Deterministic tie-breaking for reproducible plans.
41
+ */
42
+ class Frontier {
43
+ constructor() {
44
+ /** @type {{ f: number, g: number, order: number, key: string, state: unknown, action: unknown, parentKey: string|null }[]} */
45
+ this.heap = [];
46
+ }
47
+
48
+ get size() {
49
+ return this.heap.length;
50
+ }
51
+
52
+ /**
53
+ * @param {{ f: number, g: number, order: number, key: string, state: unknown, action: unknown, parentKey: string|null }} node
54
+ */
55
+ push(node) {
56
+ this.heap.push(node);
57
+ this.#siftUp(this.heap.length - 1);
58
+ }
59
+
60
+ pop() {
61
+ const heap = this.heap;
62
+ if (!heap.length) return null;
63
+ const top = heap[0];
64
+ const last = heap.pop();
65
+ if (heap.length && last) {
66
+ heap[0] = last;
67
+ this.#siftDown(0);
68
+ }
69
+ return top;
70
+ }
71
+
72
+ /**
73
+ * @param {number} i
74
+ */
75
+ #siftUp(i) {
76
+ const heap = this.heap;
77
+ while (i > 0) {
78
+ const p = (i - 1) >> 1;
79
+ if (compareNodes(heap[i], heap[p]) >= 0) break;
80
+ [heap[i], heap[p]] = [heap[p], heap[i]];
81
+ i = p;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * @param {number} i
87
+ */
88
+ #siftDown(i) {
89
+ const heap = this.heap;
90
+ const n = heap.length;
91
+ while (true) {
92
+ let best = i;
93
+ const l = i * 2 + 1;
94
+ const r = l + 1;
95
+ if (l < n && compareNodes(heap[l], heap[best]) < 0) best = l;
96
+ if (r < n && compareNodes(heap[r], heap[best]) < 0) best = r;
97
+ if (best === i) break;
98
+ [heap[i], heap[best]] = [heap[best], heap[i]];
99
+ i = best;
100
+ }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * @param {{ f: number, g: number, order: number, key: string }} a
106
+ * @param {{ f: number, g: number, order: number, key: string }} b
107
+ */
108
+ function compareNodes(a, b) {
109
+ if (a.f !== b.f) return a.f - b.f;
110
+ // Prefer higher g when f ties (deeper / more committed path) — classic A* tie-break.
111
+ if (a.g !== b.g) return b.g - a.g;
112
+ if (a.order !== b.order) return a.order - b.order;
113
+ if (a.key < b.key) return -1;
114
+ if (a.key > b.key) return 1;
115
+ return 0;
116
+ }
117
+
118
+ /**
119
+ * @param {object} opts
120
+ * @param {unknown} opts.initialState
121
+ * @param {(state: unknown) => boolean} opts.isGoal
122
+ * @param {(state: unknown) => Array<{ action: unknown, nextState: unknown, cost?: number }>} opts.expand
123
+ * @param {(state: unknown) => number} [opts.heuristic]
124
+ * @param {(state: unknown) => string} [opts.stateKey]
125
+ * @param {number} [opts.maxExpansions]
126
+ * @param {AbortSignal} [opts.signal]
127
+ * @returns {AStarResult}
128
+ */
129
+ export function planAStar(opts = {}) {
130
+ const started = Date.now();
131
+ const {
132
+ initialState,
133
+ isGoal,
134
+ expand,
135
+ heuristic = () => 0,
136
+ stateKey = stableStateKey,
137
+ maxExpansions = Number.POSITIVE_INFINITY,
138
+ signal,
139
+ } = opts;
140
+
141
+ /** @type {AStarResult} */
142
+ const base = {
143
+ ok: false,
144
+ status: 'invalid_input',
145
+ plan: [],
146
+ total_cost: 0,
147
+ expanded_states: 0,
148
+ generated_states: 0,
149
+ frontier_peak: 0,
150
+ elapsed_ms: 0,
151
+ engine: ASTAR_ENGINE,
152
+ };
153
+
154
+ if (typeof isGoal !== 'function' || typeof expand !== 'function') {
155
+ return finish(base, 'invalid_input', { error: 'isGoal and expand are required functions' }, started);
156
+ }
157
+ if (typeof heuristic !== 'function' || typeof stateKey !== 'function') {
158
+ return finish(base, 'invalid_input', { error: 'heuristic and stateKey must be functions' }, started);
159
+ }
160
+ if (!(typeof maxExpansions === 'number' && maxExpansions >= 0)) {
161
+ return finish(base, 'invalid_input', { error: 'maxExpansions must be a non-negative number' }, started);
162
+ }
163
+
164
+ if (signal?.aborted) {
165
+ return finish(base, 'cancelled', { error: 'aborted_before_start' }, started);
166
+ }
167
+
168
+ const startKey = stateKey(initialState);
169
+ const open = new Frontier();
170
+ /** @type {Map<string, number>} */
171
+ const bestG = new Map();
172
+ /** @type {Map<string, { parentKey: string|null, action: unknown, state: unknown, g: number }>} */
173
+ const cameFrom = new Map();
174
+
175
+ let order = 0;
176
+ const h0 = Math.max(0, Number(heuristic(initialState)) || 0);
177
+ open.push({
178
+ f: h0,
179
+ g: 0,
180
+ order: order++,
181
+ key: startKey,
182
+ state: initialState,
183
+ action: null,
184
+ parentKey: null,
185
+ });
186
+ bestG.set(startKey, 0);
187
+ cameFrom.set(startKey, { parentKey: null, action: null, state: initialState, g: 0 });
188
+ base.generated_states = 1;
189
+ base.frontier_peak = 1;
190
+
191
+ while (open.size > 0) {
192
+ if (signal?.aborted) {
193
+ return finish(base, 'cancelled', { error: 'aborted' }, started);
194
+ }
195
+
196
+ const current = open.pop();
197
+ if (!current) break;
198
+
199
+ const knownG = bestG.get(current.key);
200
+ // Superseded entry still in heap — skip.
201
+ if (knownG !== undefined && current.g > knownG) continue;
202
+
203
+ if (isGoal(current.state)) {
204
+ const plan = reconstructPlan(cameFrom, current.key);
205
+ return finish(base, 'found', {
206
+ ok: true,
207
+ plan,
208
+ total_cost: current.g,
209
+ final_state: current.state,
210
+ }, started);
211
+ }
212
+
213
+ if (base.expanded_states >= maxExpansions) {
214
+ return finish(base, 'budget_exhausted', {
215
+ error: `maxExpansions=${maxExpansions}`,
216
+ final_state: current.state,
217
+ }, started);
218
+ }
219
+
220
+ base.expanded_states += 1;
221
+
222
+ let successors;
223
+ try {
224
+ successors = expand(current.state);
225
+ } catch (err) {
226
+ return finish(base, 'invalid_input', {
227
+ error: err instanceof Error ? err.message : String(err),
228
+ }, started);
229
+ }
230
+
231
+ if (!Array.isArray(successors)) {
232
+ return finish(base, 'invalid_input', { error: 'expand must return an array' }, started);
233
+ }
234
+
235
+ for (const step of successors) {
236
+ if (!step || typeof step !== 'object') continue;
237
+ const cost = Number(step.cost);
238
+ const stepCost = Number.isFinite(cost) && cost >= 0 ? cost : 1;
239
+ const nextState = step.nextState;
240
+ const nextKey = stateKey(nextState);
241
+ const tentativeG = current.g + stepCost;
242
+ const prior = bestG.get(nextKey);
243
+ if (prior !== undefined && tentativeG >= prior) continue;
244
+
245
+ bestG.set(nextKey, tentativeG);
246
+ cameFrom.set(nextKey, {
247
+ parentKey: current.key,
248
+ action: step.action,
249
+ state: nextState,
250
+ g: tentativeG,
251
+ });
252
+ const h = Math.max(0, Number(heuristic(nextState)) || 0);
253
+ open.push({
254
+ f: tentativeG + h,
255
+ g: tentativeG,
256
+ order: order++,
257
+ key: nextKey,
258
+ state: nextState,
259
+ action: step.action,
260
+ parentKey: current.key,
261
+ });
262
+ base.generated_states += 1;
263
+ if (open.size > base.frontier_peak) base.frontier_peak = open.size;
264
+ }
265
+ }
266
+
267
+ return finish(base, 'no_plan', { error: 'frontier_empty' }, started);
268
+ }
269
+
270
+ /**
271
+ * @param {Map<string, { parentKey: string|null, action: unknown, state: unknown, g: number }>} cameFrom
272
+ * @param {string} goalKey
273
+ * @returns {AStarStep[]}
274
+ */
275
+ function reconstructPlan(cameFrom, goalKey) {
276
+ /** @type {AStarStep[]} */
277
+ const steps = [];
278
+ let key = goalKey;
279
+ while (true) {
280
+ const node = cameFrom.get(key);
281
+ if (!node || node.parentKey === null) break;
282
+ const parent = cameFrom.get(node.parentKey);
283
+ const stepCost = parent ? node.g - parent.g : node.g;
284
+ steps.push({
285
+ action: node.action,
286
+ state: node.state,
287
+ cost: stepCost,
288
+ });
289
+ key = node.parentKey;
290
+ }
291
+ steps.reverse();
292
+ return steps;
293
+ }
294
+
295
+ /**
296
+ * @param {AStarResult} base
297
+ * @param {AStarStatus} status
298
+ * @param {Partial<AStarResult>} extra
299
+ * @param {number} started
300
+ * @returns {AStarResult}
301
+ */
302
+ function finish(base, status, extra, started) {
303
+ return {
304
+ ...base,
305
+ ...extra,
306
+ status,
307
+ ok: status === 'found',
308
+ elapsed_ms: Date.now() - started,
309
+ engine: ASTAR_ENGINE,
310
+ };
311
+ }