@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,177 @@
1
+ /**
2
+ * Thin GOAP adapter over planAStar.
3
+ * Turns precondition/effect action catalogs into an A* search space.
4
+ */
5
+
6
+ import { planAStar, ASTAR_ENGINE } from './astar.js';
7
+ import {
8
+ applyEffects,
9
+ cloneState,
10
+ goalSatisfied,
11
+ preconditionsMet,
12
+ stableStateKey,
13
+ } from './state.js';
14
+
15
+ export const GOAP_ENGINE = 'sam-goap-astar-v1';
16
+
17
+ /**
18
+ * @typedef {object} GoapAction
19
+ * @property {string} id
20
+ * @property {number} [cost]
21
+ * @property {Record<string, unknown>} [preconditions]
22
+ * @property {Record<string, unknown>} [effects]
23
+ */
24
+
25
+ /**
26
+ * @param {object} opts
27
+ * @param {Record<string, unknown>} opts.initialState
28
+ * @param {Record<string, unknown>} opts.goal
29
+ * @param {GoapAction[]} opts.actions
30
+ * @param {(state: Record<string, unknown>) => number} [opts.heuristic]
31
+ * @param {number} [opts.maxExpansions]
32
+ * @param {AbortSignal} [opts.signal]
33
+ */
34
+ export function planGoap(opts = {}) {
35
+ const {
36
+ initialState,
37
+ goal,
38
+ actions,
39
+ heuristic,
40
+ maxExpansions,
41
+ signal,
42
+ } = opts;
43
+
44
+ if (!initialState || typeof initialState !== 'object' || Array.isArray(initialState)) {
45
+ return {
46
+ ok: false,
47
+ status: 'invalid_input',
48
+ plan: [],
49
+ action_ids: [],
50
+ total_cost: 0,
51
+ expanded_states: 0,
52
+ generated_states: 0,
53
+ frontier_peak: 0,
54
+ elapsed_ms: 0,
55
+ engine: GOAP_ENGINE,
56
+ error: 'initialState must be a plain object',
57
+ };
58
+ }
59
+ if (!goal || typeof goal !== 'object' || Array.isArray(goal)) {
60
+ return {
61
+ ok: false,
62
+ status: 'invalid_input',
63
+ plan: [],
64
+ action_ids: [],
65
+ total_cost: 0,
66
+ expanded_states: 0,
67
+ generated_states: 0,
68
+ frontier_peak: 0,
69
+ elapsed_ms: 0,
70
+ engine: GOAP_ENGINE,
71
+ error: 'goal must be a plain object',
72
+ };
73
+ }
74
+ if (!Array.isArray(actions)) {
75
+ return {
76
+ ok: false,
77
+ status: 'invalid_input',
78
+ plan: [],
79
+ action_ids: [],
80
+ total_cost: 0,
81
+ expanded_states: 0,
82
+ generated_states: 0,
83
+ frontier_peak: 0,
84
+ elapsed_ms: 0,
85
+ engine: GOAP_ENGINE,
86
+ error: 'actions must be an array',
87
+ };
88
+ }
89
+
90
+ // Normalize once; never mutate caller arrays/objects during search.
91
+ const catalog = actions.map((action, index) => {
92
+ if (!action || typeof action !== 'object') {
93
+ throw new TypeError(`goap_action_invalid:${index}`);
94
+ }
95
+ const id = typeof action.id === 'string' && action.id ? action.id : `action_${index}`;
96
+ const cost = Number(action.cost);
97
+ return {
98
+ id,
99
+ cost: Number.isFinite(cost) && cost >= 0 ? cost : 1,
100
+ preconditions: action.preconditions && typeof action.preconditions === 'object'
101
+ ? { ...action.preconditions }
102
+ : {},
103
+ effects: action.effects && typeof action.effects === 'object'
104
+ ? { ...action.effects }
105
+ : {},
106
+ };
107
+ });
108
+
109
+ // Deterministic expansion order: cost asc, then id.
110
+ catalog.sort((a, b) => (a.cost - b.cost) || a.id.localeCompare(b.id));
111
+
112
+ const start = cloneState(initialState);
113
+ const goalFacts = { ...goal };
114
+
115
+ let result;
116
+ try {
117
+ result = planAStar({
118
+ initialState: start,
119
+ isGoal: (state) => goalSatisfied(/** @type {Record<string, unknown>} */ (state), goalFacts),
120
+ expand: (state) => {
121
+ const world = /** @type {Record<string, unknown>} */ (state);
122
+ /** @type {Array<{ action: string, nextState: Record<string, unknown>, cost: number }>} */
123
+ const out = [];
124
+ for (const action of catalog) {
125
+ if (!preconditionsMet(world, action.preconditions)) continue;
126
+ const nextState = applyEffects(world, action.effects);
127
+ // Skip no-ops that do not change state (avoids infinite self-loops).
128
+ if (stableStateKey(nextState) === stableStateKey(world)) continue;
129
+ out.push({
130
+ action: action.id,
131
+ nextState,
132
+ cost: action.cost,
133
+ });
134
+ }
135
+ return out;
136
+ },
137
+ heuristic: typeof heuristic === 'function'
138
+ ? (state) => heuristic(/** @type {Record<string, unknown>} */ (state))
139
+ : () => 0,
140
+ stateKey: stableStateKey,
141
+ maxExpansions,
142
+ signal,
143
+ });
144
+ } catch (err) {
145
+ return {
146
+ ok: false,
147
+ status: 'invalid_input',
148
+ plan: [],
149
+ action_ids: [],
150
+ total_cost: 0,
151
+ expanded_states: 0,
152
+ generated_states: 0,
153
+ frontier_peak: 0,
154
+ elapsed_ms: 0,
155
+ engine: GOAP_ENGINE,
156
+ error: err instanceof Error ? err.message : String(err),
157
+ };
158
+ }
159
+
160
+ const actionIds = result.plan.map((step) => String(step.action));
161
+ return {
162
+ ok: result.ok,
163
+ status: result.status,
164
+ plan: actionIds,
165
+ action_ids: actionIds,
166
+ total_cost: result.total_cost,
167
+ cost: result.total_cost,
168
+ final_state: result.final_state,
169
+ expanded_states: result.expanded_states,
170
+ generated_states: result.generated_states,
171
+ frontier_peak: result.frontier_peak,
172
+ elapsed_ms: result.elapsed_ms,
173
+ engine: GOAP_ENGINE,
174
+ astar_engine: ASTAR_ENGINE,
175
+ error: result.error,
176
+ };
177
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * SAM planning primitives — Systematic Autonomous Machinery.
3
+ */
4
+
5
+ export {
6
+ planAStar,
7
+ ASTAR_ENGINE,
8
+ } from './astar.js';
9
+
10
+ export {
11
+ planGoap,
12
+ GOAP_ENGINE,
13
+ } from './goap.js';
14
+
15
+ export {
16
+ stableStateKey,
17
+ cloneState,
18
+ applyEffects,
19
+ goalSatisfied,
20
+ preconditionsMet,
21
+ } from './state.js';
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Deterministic state helpers for SAM planners.
3
+ * Pure machine utilities — no D1, model, or repository assumptions.
4
+ */
5
+
6
+ /**
7
+ * Stable deterministic identity for plain object / array / primitive state.
8
+ * Object keys are sorted so insertion order does not affect identity.
9
+ * @param {unknown} value
10
+ * @returns {string}
11
+ */
12
+ export function stableStateKey(value) {
13
+ return stringifyStable(value);
14
+ }
15
+
16
+ /**
17
+ * Shallow clone of a plain object state (GOAP worlds are flat fact maps).
18
+ * @param {Record<string, unknown>|null|undefined} state
19
+ * @returns {Record<string, unknown>}
20
+ */
21
+ export function cloneState(state) {
22
+ if (!state || typeof state !== 'object' || Array.isArray(state)) {
23
+ return {};
24
+ }
25
+ return { ...state };
26
+ }
27
+
28
+ /**
29
+ * Merge effects onto a clone of state (never mutates input).
30
+ * @param {Record<string, unknown>} state
31
+ * @param {Record<string, unknown>|null|undefined} effects
32
+ * @returns {Record<string, unknown>}
33
+ */
34
+ export function applyEffects(state, effects) {
35
+ const next = cloneState(state);
36
+ if (!effects || typeof effects !== 'object') return next;
37
+ for (const [key, value] of Object.entries(effects)) {
38
+ next[key] = value;
39
+ }
40
+ return next;
41
+ }
42
+
43
+ /**
44
+ * True when every desired goal fact matches current state (===).
45
+ * @param {Record<string, unknown>} state
46
+ * @param {Record<string, unknown>|null|undefined} goal
47
+ */
48
+ export function goalSatisfied(state, goal) {
49
+ if (!goal || typeof goal !== 'object') return true;
50
+ return Object.entries(goal).every(([key, value]) => state[key] === value);
51
+ }
52
+
53
+ /**
54
+ * True when every precondition matches current state (===).
55
+ * @param {Record<string, unknown>} state
56
+ * @param {Record<string, unknown>|null|undefined} preconditions
57
+ */
58
+ export function preconditionsMet(state, preconditions) {
59
+ if (!preconditions || typeof preconditions !== 'object') return true;
60
+ return Object.entries(preconditions).every(([key, value]) => state[key] === value);
61
+ }
62
+
63
+ /**
64
+ * @param {unknown} value
65
+ * @returns {string}
66
+ */
67
+ function stringifyStable(value) {
68
+ if (value === null) return 'null';
69
+ const t = typeof value;
70
+ if (t === 'undefined') return 'undefined';
71
+ if (t === 'number' || t === 'boolean' || t === 'bigint') return String(value);
72
+ if (t === 'string') return JSON.stringify(value);
73
+ if (t === 'symbol') return value.toString();
74
+ if (typeof value === 'function') return `"fn:${value.name || 'anonymous'}"`;
75
+ if (Array.isArray(value)) {
76
+ return `[${value.map((item) => stringifyStable(item)).join(',')}]`;
77
+ }
78
+ if (value instanceof Date) return JSON.stringify(value.toISOString());
79
+ if (t === 'object') {
80
+ const keys = Object.keys(value).sort();
81
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stringifyStable(value[k])}`).join(',')}}`;
82
+ }
83
+ return JSON.stringify(String(value));
84
+ }
@@ -0,0 +1,48 @@
1
+ /** @type {Map<string, import('./types.js').SamOperationDef>} */
2
+ const operations = new Map();
3
+
4
+ /**
5
+ * @param {import('./types.js').SamOperationDef} def
6
+ */
7
+ export function registerSamOperation(def) {
8
+ if (operations.has(def.id)) {
9
+ throw new Error(`sam_operation_already_registered:${def.id}`);
10
+ }
11
+ operations.set(def.id, def);
12
+ return def;
13
+ }
14
+
15
+ /**
16
+ * @param {string} id
17
+ */
18
+ export function getSamOperation(id) {
19
+ return operations.get(id) || null;
20
+ }
21
+
22
+ export function listSamOperations() {
23
+ return [...operations.values()].sort((a, b) => a.id.localeCompare(b.id));
24
+ }
25
+
26
+ export function clearSamRegistryForTests() {
27
+ operations.clear();
28
+ }
29
+
30
+ /**
31
+ * Compact cards for discover / tool selection.
32
+ * @param {import('./types.js').SamOperationDef} def
33
+ */
34
+ export function toSamOperationCard(def) {
35
+ return {
36
+ id: def.id,
37
+ module: def.module,
38
+ action: def.action,
39
+ summary: def.summary,
40
+ model: def.execution.model,
41
+ risk: def.risk,
42
+ network: def.execution.network,
43
+ side_effects: def.execution.sideEffects,
44
+ lanes: [...def.execution.lanes],
45
+ status: def.status || 'stable',
46
+ cli: def.cli?.command || [],
47
+ };
48
+ }
@@ -0,0 +1,77 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { SAM_RESULT_SCHEMA } from './types.js';
3
+
4
+ /**
5
+ * @param {unknown} value
6
+ */
7
+ export function hashJson(value) {
8
+ try {
9
+ return createHash('sha256').update(JSON.stringify(value)).digest('hex');
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+
15
+ /**
16
+ * @param {object} opts
17
+ * @param {string} opts.operation
18
+ * @param {string} [opts.module]
19
+ * @param {boolean} opts.ok
20
+ * @param {unknown} opts.data
21
+ * @param {string} opts.startedAt
22
+ * @param {import('./types.js').SamLane} [opts.lane]
23
+ * @param {boolean} [opts.deterministic]
24
+ * @param {boolean} [opts.modelUsed]
25
+ * @param {string[]} [opts.sideEffects]
26
+ * @param {unknown} [opts.input]
27
+ * @param {unknown} [opts.error]
28
+ * @param {unknown[]} [opts.warnings]
29
+ * @param {import('./types.js').SamReceipt['status']} [opts.status]
30
+ * @returns {import('./types.js').SamResult}
31
+ */
32
+ export function buildSamResult({
33
+ operation,
34
+ module,
35
+ ok,
36
+ data,
37
+ startedAt,
38
+ lane = 'local',
39
+ deterministic = true,
40
+ modelUsed = false,
41
+ sideEffects = [],
42
+ input,
43
+ error,
44
+ warnings,
45
+ status,
46
+ }) {
47
+ const completedAt = new Date().toISOString();
48
+ /** @type {import('./types.js').SamResult} */
49
+ const result = {
50
+ schema: SAM_RESULT_SCHEMA,
51
+ operation,
52
+ ok,
53
+ data: ok ? data : (data ?? null),
54
+ receipt: {
55
+ id: `samr_${randomUUID().replace(/-/g, '').slice(0, 24)}`,
56
+ operation,
57
+ module,
58
+ started_at: startedAt,
59
+ completed_at: completedAt,
60
+ execution: {
61
+ lane,
62
+ deterministic,
63
+ model_used: modelUsed,
64
+ },
65
+ side_effects: sideEffects,
66
+ input_hash: input === undefined ? null : hashJson(input),
67
+ output_hash: ok ? hashJson(data) : null,
68
+ status: status || (ok ? 'completed' : 'failed'),
69
+ },
70
+ };
71
+ if (warnings?.length) result.warnings = warnings;
72
+ if (error !== undefined) result.error = error;
73
+ if (!modelUsed) {
74
+ result.usage = { provider_calls: 0, cost_usd: 0 };
75
+ }
76
+ return result;
77
+ }
@@ -0,0 +1,44 @@
1
+ import { registerSamOperation, getSamOperation } from './registry.js';
2
+ import { repositoryInspect } from './operations/repository-inspect.js';
3
+ import { brandScanOp } from './operations/brand-scan.js';
4
+ import { securityScanOp } from './operations/security-scan.js';
5
+ import { terminalExecOp } from './operations/terminal-exec.js';
6
+ import { cadBlenderInspectOp } from './operations/cad-blender-inspect.js';
7
+ import { codebaseindexIngestOp } from './operations/codebaseindex-ingest.js';
8
+ import { planningAstarOp } from './operations/planning-astar.js';
9
+ import { planningGoapOp } from './operations/planning-goap.js';
10
+ import { decisionEvaluateOp } from './operations/decision-evaluate.js';
11
+
12
+ export const SEED_OPERATIONS = [
13
+ repositoryInspect,
14
+ brandScanOp,
15
+ securityScanOp,
16
+ terminalExecOp,
17
+ cadBlenderInspectOp,
18
+ codebaseindexIngestOp,
19
+ planningAstarOp,
20
+ planningGoapOp,
21
+ decisionEvaluateOp,
22
+ ];
23
+
24
+ let seeded = false;
25
+
26
+ /**
27
+ * Register seed SAM operations (idempotent).
28
+ * Handlers reuse existing capability implementations — no duplicate machinery.
29
+ */
30
+ export function ensureSeedOperations() {
31
+ if (seeded) return SEED_OPERATIONS;
32
+ for (const op of SEED_OPERATIONS) {
33
+ if (!getSamOperation(op.id)) {
34
+ registerSamOperation(op);
35
+ }
36
+ }
37
+ seeded = true;
38
+ return SEED_OPERATIONS;
39
+ }
40
+
41
+ /** @internal */
42
+ export function resetSeedFlagForTests() {
43
+ seeded = false;
44
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * SAM — Systematic Autonomous Machinery
3
+ * @see docs/architecture/SAM_KERNEL.md
4
+ */
5
+
6
+ export const SAM_RESULT_SCHEMA = 'agentsam.result.v1';
7
+ export const SAM_EXPANSION = 'Systematic Autonomous Machinery';
8
+
9
+ /** @typedef {'local'|'remote'|'sandbox'|'platform'} SamLane */
10
+ /** @typedef {'never'|'optional'|'required'} SamModelPolicy */
11
+ /** @typedef {'none'|'optional'|'required'} SamNetworkPolicy */
12
+ /** @typedef {'none'|'local_write'|'remote_read'|'remote_write'|'deploy'|'billable'} SamSideEffects */
13
+ /** @typedef {'read_only'|'write'|'privileged'} SamRisk */
14
+ /** @typedef {'never'|'optional'|'required'} SamSpendPolicy */
15
+
16
+ /**
17
+ * @typedef {object} SamExecutionMeta
18
+ * @property {SamLane[]} lanes
19
+ * @property {SamModelPolicy} model
20
+ * @property {SamNetworkPolicy} network
21
+ * @property {SamSideEffects} sideEffects
22
+ * @property {SamSpendPolicy} [embedding]
23
+ * @property {SamSpendPolicy} [provider_spend]
24
+ */
25
+
26
+ /**
27
+ * Rich product capability contract — not just catalog cards.
28
+ * @typedef {object} SamOperationDef
29
+ * @property {string} id
30
+ * @property {number} version
31
+ * @property {string} module
32
+ * @property {string} action
33
+ * @property {string} summary
34
+ * @property {string} [purpose]
35
+ * @property {string} [outcome]
36
+ * @property {string} [description]
37
+ * @property {SamExecutionMeta} execution
38
+ * @property {{ account?: boolean, provider?: string[] }} [auth]
39
+ * @property {SamRisk} risk
40
+ * @property {string} [input_schema]
41
+ * @property {string} [output_schema]
42
+ * @property {string[]} [capabilities]
43
+ * @property {string[]} [accepts]
44
+ * @property {string[]} [phases]
45
+ * @property {string[]} [artifacts]
46
+ * @property {{ id?: string, help?: boolean }|string|null} [skill]
47
+ * @property {{ available?: boolean, operation?: string }} [preview]
48
+ * @property {{ command?: string[][] }} [cli]
49
+ * @property {{ section?: string, examples?: string[] }} [docs]
50
+ * @property {'stable'|'experimental'|'stub'|'deprecated'} [status]
51
+ * @property {(input: unknown, ctx: SamOperationContext) => Promise<unknown>|unknown} handler
52
+ */
53
+
54
+ /**
55
+ * @typedef {object} SamOperationContext
56
+ * @property {AbortSignal} [signal]
57
+ * @property {Record<string, unknown>} [execution]
58
+ * @property {string} [cwd]
59
+ * @property {Record<string, string|undefined>} [env]
60
+ */
61
+
62
+ /**
63
+ * @typedef {object} SamReceipt
64
+ * @property {string} id
65
+ * @property {string} operation
66
+ * @property {string} [module]
67
+ * @property {string} started_at
68
+ * @property {string} completed_at
69
+ * @property {{ lane: SamLane, deterministic: boolean, model_used: boolean }} execution
70
+ * @property {string[]} [side_effects]
71
+ * @property {string|null} [input_hash]
72
+ * @property {string|null} [output_hash]
73
+ * @property {'completed'|'failed'|'cancelled'} status
74
+ */
75
+
76
+ /**
77
+ * @typedef {object} SamResult
78
+ * @property {'agentsam.result.v1'} schema
79
+ * @property {string} operation
80
+ * @property {number} [operation_version]
81
+ * @property {boolean} ok
82
+ * @property {unknown} data
83
+ * @property {SamReceipt} receipt
84
+ * @property {unknown[]} [evidence]
85
+ * @property {unknown[]} [artifacts]
86
+ * @property {{ provider_calls: number, model_calls?: number, embedding_calls?: number, cost_usd?: number }} [usage]
87
+ * @property {{ skill?: string|null }} [help]
88
+ * @property {unknown[]} [warnings]
89
+ * @property {unknown[]} [diagnostics]
90
+ * @property {unknown} [error]
91
+ */
@@ -0,0 +1,64 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
6
+ const skillsRoot = path.resolve(moduleDir, '..', '..', 'skills');
7
+ const catalogPath = path.join(skillsRoot, 'catalog.json');
8
+
9
+ function readCatalog() {
10
+ return JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
11
+ }
12
+
13
+ function normalizeSkillId(value) {
14
+ return String(value ?? '').trim().toLowerCase();
15
+ }
16
+
17
+ function resolveSkillRecord(idOrAlias) {
18
+ const needle = normalizeSkillId(idOrAlias);
19
+ if (!needle) return null;
20
+ const catalog = readCatalog();
21
+ return catalog.skills.find((skill) => {
22
+ if (normalizeSkillId(skill.id) === needle) return true;
23
+ return (skill.aliases ?? []).some((alias) => normalizeSkillId(alias) === needle);
24
+ }) ?? null;
25
+ }
26
+
27
+ function safeSkillPath(relativePath) {
28
+ const resolved = path.resolve(skillsRoot, relativePath);
29
+ const rootWithSep = `${skillsRoot}${path.sep}`;
30
+ if (resolved !== skillsRoot && !resolved.startsWith(rootWithSep)) {
31
+ throw new Error('skill_path_outside_root');
32
+ }
33
+ return resolved;
34
+ }
35
+
36
+ export function listSkills() {
37
+ return readCatalog().skills.map((skill) => ({ ...skill }));
38
+ }
39
+
40
+ export function getSkill(idOrAlias) {
41
+ const skill = resolveSkillRecord(idOrAlias);
42
+ return skill ? { ...skill } : null;
43
+ }
44
+
45
+ export function loadSkill(idOrAlias, options = {}) {
46
+ const skill = resolveSkillRecord(idOrAlias);
47
+ if (!skill) throw new Error(`unknown_skill:${idOrAlias}`);
48
+
49
+ const result = {
50
+ skill: { ...skill },
51
+ instructions: fs.readFileSync(safeSkillPath(skill.entry), 'utf8'),
52
+ };
53
+
54
+ if (options.references === true) {
55
+ result.references = (skill.references ?? []).map((relativePath) => ({
56
+ path: relativePath,
57
+ content: fs.readFileSync(safeSkillPath(relativePath), 'utf8'),
58
+ }));
59
+ }
60
+
61
+ return result;
62
+ }
63
+
64
+ export const AGENTSAM_SKILLS_ROOT = skillsRoot;