@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,183 @@
1
+ /**
2
+ * AgentSam activity event stream — one run, many surfaces (Studio / CLI / desktop).
3
+ * Scene/status/progress come ONLY from these events — never demo timers.
4
+ */
5
+
6
+ export const ACTIVITY_SCHEMA = 'agentsam.activity.v1';
7
+
8
+ export const ACTIVITY_PHASES = Object.freeze([
9
+ 'boot',
10
+ 'observe',
11
+ 'context',
12
+ 'plan',
13
+ 'execute',
14
+ 'verify',
15
+ 'compact',
16
+ 'waiting',
17
+ 'recover',
18
+ 'complete',
19
+ ]);
20
+
21
+ export const ACTIVITY_EVENTS = Object.freeze([
22
+ 'run.started',
23
+ 'phase.changed',
24
+ 'step.started',
25
+ 'step.progress',
26
+ 'step.completed',
27
+ 'decision.made',
28
+ 'tool.started',
29
+ 'tool.completed',
30
+ 'artifact.created',
31
+ 'file.changed',
32
+ 'approval.required',
33
+ 'input.required',
34
+ 'verification.completed',
35
+ 'run.failed',
36
+ 'run.completed',
37
+ ]);
38
+
39
+ /** User-facing labels — never expose choose/score/check as primary copy. */
40
+ export const DECISION_ACTIVITY_LABELS = Object.freeze({
41
+ terminal_lane: 'Selecting execution path',
42
+ retrieval_mode: 'Choosing retrieval path',
43
+ change_risk: 'Assessing change risk',
44
+ requires_approval: 'Checking approval requirements',
45
+ security_review_required: 'Checking security review requirements',
46
+ verification_scope: 'Selecting verification scope',
47
+ skill_candidate: 'Selecting skill',
48
+ });
49
+
50
+ /**
51
+ * @param {object} partial
52
+ */
53
+ export function createActivityEvent(partial = {}) {
54
+ const phase = partial.phase || 'execute';
55
+ const event = partial.event || 'step.progress';
56
+ if (!ACTIVITY_PHASES.includes(phase)) {
57
+ const err = new Error(`invalid_activity_phase:${phase}`);
58
+ err.code = 'invalid_activity_phase';
59
+ throw err;
60
+ }
61
+ if (!ACTIVITY_EVENTS.includes(event)) {
62
+ const err = new Error(`invalid_activity_event:${event}`);
63
+ err.code = 'invalid_activity_event';
64
+ throw err;
65
+ }
66
+ return Object.freeze({
67
+ schema: ACTIVITY_SCHEMA,
68
+ run_id: String(partial.run_id || ''),
69
+ seq: Number.isInteger(partial.seq) ? partial.seq : 0,
70
+ timestamp: partial.timestamp || new Date().toISOString(),
71
+ phase,
72
+ event,
73
+ step_id: partial.step_id ?? null,
74
+ parent_id: partial.parent_id ?? null,
75
+ label: String(partial.label || ''),
76
+ detail: partial.detail ?? null,
77
+ progress: normalizeProgress(partial.progress),
78
+ source: partial.source
79
+ ? {
80
+ kind: partial.source.kind || 'sam',
81
+ name: partial.source.name ?? null,
82
+ }
83
+ : { kind: 'sam', name: null },
84
+ evidence: partial.evidence && typeof partial.evidence === 'object'
85
+ ? { ...partial.evidence }
86
+ : null,
87
+ });
88
+ }
89
+
90
+ function normalizeProgress(progress) {
91
+ if (progress == null) return null;
92
+ if (typeof progress !== 'object') return null;
93
+ const current = Number(progress.current);
94
+ const total = Number(progress.total);
95
+ if (!Number.isFinite(current) || !Number.isFinite(total) || total <= 0) return null;
96
+ return { current, total };
97
+ }
98
+
99
+ /**
100
+ * Map a decision answer to a user-visible activity event (primitives stay in evidence).
101
+ * @param {object} opts
102
+ */
103
+ export function activityFromDecision(opts = {}) {
104
+ const questionId = opts.question_id || opts.answer?.question_id;
105
+ const answer = opts.answer || {};
106
+ const label = DECISION_ACTIVITY_LABELS[questionId]
107
+ || opts.label
108
+ || 'Making a structured decision';
109
+ const valueText = formatAnswerValue(answer);
110
+ return createActivityEvent({
111
+ run_id: opts.run_id,
112
+ seq: opts.seq,
113
+ phase: opts.phase || 'plan',
114
+ event: 'decision.made',
115
+ step_id: opts.step_id ?? null,
116
+ label,
117
+ detail: valueText,
118
+ source: { kind: 'sam', name: answer.evaluator?.kind || 'decision' },
119
+ evidence: {
120
+ decision_id: opts.decision_id ?? null,
121
+ question_id: questionId,
122
+ question_type: answer.type ?? null,
123
+ value: answer.value ?? null,
124
+ confidence_estimate: answer.confidence_estimate ?? null,
125
+ support: answer.support ?? null,
126
+ receipt_id: opts.decision_id ?? null,
127
+ },
128
+ });
129
+ }
130
+
131
+ function formatAnswerValue(answer) {
132
+ if (!answer || answer.value == null) return null;
133
+ if (answer.type === 'check') return answer.value ? 'Required' : 'Not required';
134
+ if (answer.type === 'score') {
135
+ const n = Number(answer.value);
136
+ return Number.isFinite(n) ? (n < 2 ? 'Bounded' : n < 3.5 ? 'Elevated' : 'High') : String(answer.value);
137
+ }
138
+ return String(answer.value);
139
+ }
140
+
141
+ /**
142
+ * In-memory run activity store — Studio SideStage and CLI subscribe to the same seq stream.
143
+ */
144
+ export function createActivityStore(runId) {
145
+ const id = String(runId || `run_${Date.now().toString(36)}`);
146
+ /** @type {object[]} */
147
+ const events = [];
148
+ /** @type {Set<(e: object) => void>} */
149
+ const listeners = new Set();
150
+ let seq = 0;
151
+
152
+ function emit(partial) {
153
+ const event = createActivityEvent({
154
+ ...partial,
155
+ run_id: id,
156
+ seq: ++seq,
157
+ });
158
+ events.push(event);
159
+ for (const fn of listeners) {
160
+ try {
161
+ fn(event);
162
+ } catch {
163
+ /* subscriber errors must not break the run */
164
+ }
165
+ }
166
+ return event;
167
+ }
168
+
169
+ return {
170
+ run_id: id,
171
+ emit,
172
+ on(fn) {
173
+ listeners.add(fn);
174
+ return () => listeners.delete(fn);
175
+ },
176
+ snapshot() {
177
+ return [...events];
178
+ },
179
+ latest() {
180
+ return events[events.length - 1] || null;
181
+ },
182
+ };
183
+ }
@@ -0,0 +1,252 @@
1
+ import { getSamOperation, listSamOperations, toSamOperationCard } from './registry.js';
2
+ import { buildSamResult } from './result.js';
3
+ import { ensureSeedOperations } from './seed.js';
4
+
5
+ /**
6
+ * Resolve root/cwd from common input shapes.
7
+ * @param {unknown} input
8
+ */
9
+ function resolveRoot(input) {
10
+ if (input == null) return process.cwd();
11
+ if (typeof input === 'string') return input;
12
+ if (typeof input === 'object') {
13
+ const o = /** @type {Record<string, unknown>} */ (input);
14
+ if (typeof o.root === 'string') return o.root;
15
+ if (typeof o.cwd === 'string') return o.cwd;
16
+ if (typeof o.projectRoot === 'string') return o.projectRoot;
17
+ }
18
+ return process.cwd();
19
+ }
20
+
21
+ /**
22
+ * AgentSamClient — public facade over Systematic Autonomous Machinery (SAM).
23
+ *
24
+ * `sam` is the conventional variable name for this client. It is machinery,
25
+ * not a human identity.
26
+ *
27
+ * @example
28
+ * import { AgentSamClient } from '@inneranimalmedia/agentsam-sdk';
29
+ * const sam = new AgentSamClient();
30
+ * const result = await sam.invoke('repository.inspect', { root: '.' });
31
+ */
32
+ export class AgentSamClient {
33
+ /**
34
+ * @param {object} [options]
35
+ * @param {string} [options.apiKey] Explicit platform credential (AGENTSAM_API_KEY). Local ops do not require it.
36
+ * @param {string} [options.cwd]
37
+ * @param {Record<string, string|undefined>} [options.env]
38
+ * @param {boolean} [options.autoSeed=true]
39
+ */
40
+ constructor(options = {}) {
41
+ this.apiKey = options.apiKey ?? options.env?.AGENTSAM_API_KEY ?? process.env.AGENTSAM_API_KEY ?? null;
42
+ this.cwd = options.cwd ?? process.cwd();
43
+ this.env = options.env ?? process.env;
44
+ if (options.autoSeed !== false) {
45
+ ensureSeedOperations();
46
+ }
47
+
48
+ /** Domain projections → sam.invoke(canonicalId, …) */
49
+ this.repository = {
50
+ inspect: (input = {}, opts) => this.invoke('repository.inspect', normalizeRootInput(input), opts),
51
+ };
52
+ this.brand = {
53
+ scan: (input = {}, opts) => this.invoke('brand.scan', normalizeRootInput(input), opts),
54
+ };
55
+ this.security = {
56
+ scan: (input = {}, opts) => this.invoke('security.scan', normalizeRootInput(input), opts),
57
+ };
58
+ this.terminal = {
59
+ exec: (input = {}, opts) => this.invoke('terminal.exec', input, opts),
60
+ };
61
+ this.cad = {
62
+ blender: {
63
+ inspect: (input = {}, opts) => this.invoke('cad.blender.inspect', input, opts),
64
+ },
65
+ };
66
+ this.codebaseindex = {
67
+ ingest: (input = {}, opts) => this.invoke('codebaseindex.ingest', normalizeRootInput(input), opts),
68
+ };
69
+ this.decision = {
70
+ evaluate: (input = {}, opts) => this.invoke('decision.evaluate', input, opts),
71
+ };
72
+ this.planning = {
73
+ astar: (input = {}, opts) => this.invoke('planning.astar', input, opts),
74
+ goap: (input = {}, opts) => this.invoke('planning.goap', input, opts),
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Universal SAM execution doorway.
80
+ * @param {string} operationId
81
+ * @param {unknown} [input]
82
+ * @param {object} [options]
83
+ * @param {AbortSignal} [options.signal]
84
+ * @param {Record<string, unknown>} [options.execution]
85
+ * @returns {Promise<import('./types.js').SamResult>}
86
+ */
87
+ async invoke(operationId, input = {}, options = {}) {
88
+ ensureSeedOperations();
89
+ const def = getSamOperation(operationId);
90
+ if (!def) {
91
+ return buildSamResult({
92
+ operation: operationId,
93
+ ok: false,
94
+ data: null,
95
+ startedAt: new Date().toISOString(),
96
+ error: { code: 'sam_operation_not_found', message: `Unknown operation: ${operationId}` },
97
+ status: 'failed',
98
+ });
99
+ }
100
+
101
+ const startedAt = new Date().toISOString();
102
+ const deterministic = def.execution.model === 'never';
103
+ const sideEffects = def.execution.sideEffects === 'none' ? [] : [def.execution.sideEffects];
104
+ /** @type {import('./types.js').SamOperationContext} */
105
+ const ctx = {
106
+ signal: options.signal,
107
+ execution: options.execution,
108
+ cwd: this.cwd,
109
+ env: this.env,
110
+ };
111
+
112
+ try {
113
+ const data = await def.handler(input, ctx);
114
+ const warnings = Array.isArray(/** @type {any} */ (data)?.warnings)
115
+ ? /** @type {any} */ (data).warnings
116
+ : undefined;
117
+ return buildSamResult({
118
+ operation: def.id,
119
+ module: def.module,
120
+ ok: true,
121
+ data,
122
+ startedAt,
123
+ lane: 'local',
124
+ deterministic,
125
+ modelUsed: false,
126
+ sideEffects,
127
+ input,
128
+ warnings,
129
+ });
130
+ } catch (err) {
131
+ return buildSamResult({
132
+ operation: def.id,
133
+ module: def.module,
134
+ ok: false,
135
+ data: null,
136
+ startedAt,
137
+ lane: 'local',
138
+ deterministic,
139
+ modelUsed: false,
140
+ sideEffects,
141
+ input,
142
+ error: {
143
+ code: err?.code || 'sam_operation_failed',
144
+ message: err?.message || String(err),
145
+ },
146
+ status: options.signal?.aborted ? 'cancelled' : 'failed',
147
+ });
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Inspect operation metadata without executing.
153
+ * @param {string} operationId
154
+ * @param {{ schema?: boolean }} [options]
155
+ */
156
+ async describe(operationId, options = {}) {
157
+ ensureSeedOperations();
158
+ const def = getSamOperation(operationId);
159
+ if (!def) {
160
+ return {
161
+ ok: false,
162
+ error: { code: 'sam_operation_not_found', message: `Unknown operation: ${operationId}` },
163
+ };
164
+ }
165
+ const skillId = typeof def.skill === 'string' ? def.skill : def.skill?.id || null;
166
+ const info = {
167
+ ok: true,
168
+ id: def.id,
169
+ version: def.version,
170
+ module: def.module,
171
+ action: def.action,
172
+ summary: def.summary,
173
+ purpose: def.purpose || def.description,
174
+ outcome: def.outcome || null,
175
+ description: def.description,
176
+ execution: { ...def.execution, lanes: [...def.execution.lanes] },
177
+ auth: def.auth || {},
178
+ risk: def.risk,
179
+ accepts: def.accepts || [],
180
+ phases: def.phases || [],
181
+ artifacts: def.artifacts || [],
182
+ capabilities: def.capabilities || [],
183
+ skill: skillId,
184
+ preview: def.preview || null,
185
+ cli: def.cli || {},
186
+ docs: def.docs || {},
187
+ status: def.status || 'stable',
188
+ input_schema: def.input_schema || null,
189
+ output_schema: def.output_schema || null,
190
+ };
191
+ if (options.schema) {
192
+ info.schemas_requested = true;
193
+ }
194
+ return info;
195
+ }
196
+
197
+ /**
198
+ * Compact operation cards matching a query (substring / token match).
199
+ * @param {{ query?: string, module?: string, model?: string, limit?: number }} [query]
200
+ */
201
+ async discover(query = {}) {
202
+ ensureSeedOperations();
203
+ const q = String(query.query || '').trim().toLowerCase();
204
+ const tokens = q ? q.split(/\s+/).filter(Boolean) : [];
205
+ const limit = Number.isInteger(query.limit) ? query.limit : 20;
206
+ let cards = listSamOperations().map(toSamOperationCard);
207
+
208
+ if (query.module) {
209
+ cards = cards.filter((c) => c.module === query.module);
210
+ }
211
+ if (query.model) {
212
+ cards = cards.filter((c) => c.model === query.model);
213
+ }
214
+ if (tokens.length) {
215
+ cards = cards
216
+ .map((card) => {
217
+ const hay = `${card.id} ${card.summary} ${card.module} ${card.action}`.toLowerCase();
218
+ const score = tokens.reduce((n, t) => n + (hay.includes(t) ? 1 : 0), 0);
219
+ return { card, score };
220
+ })
221
+ .filter((x) => x.score > 0)
222
+ .sort((a, b) => b.score - a.score || a.card.id.localeCompare(b.card.id))
223
+ .map((x) => x.card);
224
+ }
225
+
226
+ return {
227
+ ok: true,
228
+ count: Math.min(cards.length, limit),
229
+ operations: cards.slice(0, limit),
230
+ };
231
+ }
232
+ }
233
+
234
+ /**
235
+ * @param {unknown} input
236
+ */
237
+ function normalizeRootInput(input) {
238
+ if (typeof input === 'string') {
239
+ return { root: input };
240
+ }
241
+ if (input && typeof input === 'object') {
242
+ const o = /** @type {Record<string, unknown>} */ (input);
243
+ const root = resolveRoot(input);
244
+ return { ...o, root, cwd: o.cwd ?? root };
245
+ }
246
+ return { root: process.cwd(), cwd: process.cwd() };
247
+ }
248
+
249
+ /** Convenience factory matching docs examples. */
250
+ export function createAgentSamClient(options) {
251
+ return new AgentSamClient(options);
252
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Calibration metrics — fixtures/known formulas.
3
+ * Do NOT claim calibrated probabilities without a stored calibration_id.
4
+ */
5
+
6
+ import { DECISION_CALIBRATION_SCHEMA } from './types.js';
7
+
8
+ /**
9
+ * Brier score for binary forecasts.
10
+ * @param {Array<{ p: number, y: 0|1|boolean }>} rows
11
+ */
12
+ export function brierScore(rows) {
13
+ if (!rows?.length) return null;
14
+ let sum = 0;
15
+ for (const row of rows) {
16
+ const p = Number(row.p);
17
+ const y = row.y === true || row.y === 1 ? 1 : 0;
18
+ sum += (p - y) ** 2;
19
+ }
20
+ return sum / rows.length;
21
+ }
22
+
23
+ /**
24
+ * Log loss for binary forecasts.
25
+ * @param {Array<{ p: number, y: 0|1|boolean }>} rows
26
+ * @param {number} [eps]
27
+ */
28
+ export function logLoss(rows, eps = 1e-15) {
29
+ if (!rows?.length) return null;
30
+ let sum = 0;
31
+ for (const row of rows) {
32
+ const y = row.y === true || row.y === 1 ? 1 : 0;
33
+ const p = Math.min(1 - eps, Math.max(eps, Number(row.p)));
34
+ sum += -(y * Math.log(p) + (1 - y) * Math.log(1 - p));
35
+ }
36
+ return sum / rows.length;
37
+ }
38
+
39
+ /**
40
+ * Expected Calibration Error with equal-width bins.
41
+ * @param {Array<{ p: number, y: 0|1|boolean }>} rows
42
+ * @param {number} [bins]
43
+ */
44
+ export function expectedCalibrationError(rows, bins = 10) {
45
+ if (!rows?.length) return null;
46
+ /** @type {Array<{ count: number, conf: number, acc: number }>} */
47
+ const bucket = Array.from({ length: bins }, () => ({ count: 0, conf: 0, acc: 0 }));
48
+ for (const row of rows) {
49
+ const p = Number(row.p);
50
+ const y = row.y === true || row.y === 1 ? 1 : 0;
51
+ const idx = Math.min(bins - 1, Math.max(0, Math.floor(p * bins)));
52
+ bucket[idx].count += 1;
53
+ bucket[idx].conf += p;
54
+ bucket[idx].acc += y;
55
+ }
56
+ let ece = 0;
57
+ const n = rows.length;
58
+ const reliability = [];
59
+ for (let i = 0; i < bins; i += 1) {
60
+ const b = bucket[i];
61
+ if (!b.count) {
62
+ reliability.push({ bin: i, count: 0, confidence: null, accuracy: null });
63
+ continue;
64
+ }
65
+ const conf = b.conf / b.count;
66
+ const acc = b.acc / b.count;
67
+ ece += (b.count / n) * Math.abs(acc - conf);
68
+ reliability.push({ bin: i, count: b.count, confidence: conf, accuracy: acc });
69
+ }
70
+ return { ece, reliability, schema: DECISION_CALIBRATION_SCHEMA };
71
+ }
72
+
73
+ /**
74
+ * Dataset export contract from receipts with outcomes (no auto-train).
75
+ * @param {object[]} receipts
76
+ */
77
+ export function exportCalibrationDataset(receipts = []) {
78
+ const rows = [];
79
+ for (const receipt of receipts) {
80
+ if (!receipt?.outcome) continue;
81
+ for (const [qid, answer] of Object.entries(receipt.answers || {})) {
82
+ if (answer.type !== 'check') continue;
83
+ const p = answer.calibrated_probability
84
+ ?? answer.support
85
+ ?? answer.confidence_estimate;
86
+ if (p == null) continue;
87
+ rows.push({
88
+ decision_id: receipt.decision_id,
89
+ question_id: qid,
90
+ question_version: receipt.question_versions?.[qid] ?? null,
91
+ evaluator: answer.evaluator || receipt.evaluator,
92
+ p: Number(p),
93
+ y: receipt.outcome.success === true
94
+ ? (answer.value ? 1 : 0)
95
+ : (receipt.outcome.actual_class != null
96
+ ? (String(receipt.outcome.actual_class) === String(answer.value) ? 1 : 0)
97
+ : null),
98
+ human_corrected: receipt.outcome.human_corrected,
99
+ calibration_id: answer.calibration_id ?? null,
100
+ });
101
+ }
102
+ }
103
+ return {
104
+ schema: DECISION_CALIBRATION_SCHEMA,
105
+ kind: 'dataset_export',
106
+ note: 'Not automatically used for training. Requires privacy review, splits, and label quality.',
107
+ rows: rows.filter((r) => r.y != null),
108
+ };
109
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Confidence / support helpers.
3
+ *
4
+ * confidence_estimate = distribution concentration (NOT P(correct))
5
+ * support = check proposition strength in [0,1]
6
+ * probabilities = only when evaluator defines a probabilistic interpretation
7
+ * calibrated_probability = only with calibration_id after empirical fit
8
+ */
9
+
10
+ /**
11
+ * Normalized entropy concentration in [0,1].
12
+ * 1 = fully concentrated on one option; 0 = uniform.
13
+ * @param {Record<string, number>} scores non-negative weights
14
+ */
15
+ export function concentrationFromScores(scores) {
16
+ const entries = Object.entries(scores || {}).filter(([, v]) => Number.isFinite(v) && v >= 0);
17
+ if (!entries.length) return 0;
18
+ const sum = entries.reduce((n, [, v]) => n + v, 0);
19
+ if (!(sum > 0)) return 0;
20
+ if (entries.length === 1) return 1;
21
+ const probs = entries.map(([, v]) => v / sum);
22
+ const entropy = -probs.reduce((n, p) => (p > 0 ? n + p * Math.log(p) : n), 0);
23
+ const maxEntropy = Math.log(entries.length);
24
+ if (!(maxEntropy > 0)) return 1;
25
+ return clamp01(1 - entropy / maxEntropy);
26
+ }
27
+
28
+ /**
29
+ * Softmax → probabilities that sum to 1 (within floating tolerance).
30
+ * @param {Record<string, number>} scores
31
+ * @param {number} [temperature]
32
+ */
33
+ export function softmax(scores, temperature = 1) {
34
+ const entries = Object.entries(scores || {});
35
+ if (!entries.length) return {};
36
+ const t = temperature > 0 ? temperature : 1;
37
+ const max = Math.max(...entries.map(([, v]) => Number(v) || 0));
38
+ const exps = entries.map(([k, v]) => [k, Math.exp(((Number(v) || 0) - max) / t)]);
39
+ const sum = exps.reduce((n, [, e]) => n + e, 0) || 1;
40
+ /** @type {Record<string, number>} */
41
+ const out = {};
42
+ for (const [k, e] of exps) out[k] = e / sum;
43
+ return out;
44
+ }
45
+
46
+ /**
47
+ * Expected score from a probability distribution over ordered levels.
48
+ * @param {Array<{ index: number }>} levels
49
+ * @param {Record<string, number>} probabilities keyed by level index string
50
+ */
51
+ export function expectedScore(levels, probabilities) {
52
+ let sum = 0;
53
+ for (const level of levels) {
54
+ const p = Number(probabilities[String(level.index)] ?? probabilities[level.index] ?? 0);
55
+ sum += level.index * (Number.isFinite(p) ? p : 0);
56
+ }
57
+ return sum;
58
+ }
59
+
60
+ /**
61
+ * Assert probabilities sum ≈ 1.
62
+ * @param {Record<string, number>} probs
63
+ * @param {number} [tolerance]
64
+ */
65
+ export function assertProbabilitiesSumToOne(probs, tolerance = 1e-6) {
66
+ const sum = Object.values(probs || {}).reduce((n, v) => n + (Number(v) || 0), 0);
67
+ if (Math.abs(sum - 1) > tolerance) {
68
+ const err = new Error(`probabilities_sum=${sum}`);
69
+ err.code = 'probabilities_invalid';
70
+ throw err;
71
+ }
72
+ return true;
73
+ }
74
+
75
+ export function clamp01(n) {
76
+ if (!Number.isFinite(n)) return 0;
77
+ if (n < 0) return 0;
78
+ if (n > 1) return 1;
79
+ return n;
80
+ }
81
+
82
+ /**
83
+ * Build answer envelope with strict raw vs calibrated labeling.
84
+ * @param {object} partial
85
+ */
86
+ export function buildAnswer(partial) {
87
+ const answer = {
88
+ schema: 'agentsam.decision-answer.v1',
89
+ type: partial.type,
90
+ question_id: partial.question_id,
91
+ value: partial.value,
92
+ scores: partial.scores ?? null,
93
+ levels: partial.levels ?? null,
94
+ support: partial.support ?? null,
95
+ confidence_estimate: partial.confidence_estimate ?? null,
96
+ probabilities: partial.probabilities ?? null,
97
+ calibrated_probability: partial.calibrated_probability ?? null,
98
+ calibration_id: partial.calibration_id ?? null,
99
+ evaluator: partial.evaluator ?? null,
100
+ evidence: Array.isArray(partial.evidence) ? partial.evidence : [],
101
+ warnings: Array.isArray(partial.warnings) ? partial.warnings : [],
102
+ };
103
+
104
+ if (answer.probabilities) {
105
+ assertProbabilitiesSumToOne(answer.probabilities);
106
+ }
107
+ if (answer.calibrated_probability != null && !answer.calibration_id) {
108
+ const err = new Error('calibrated_probability requires calibration_id');
109
+ err.code = 'calibration_required';
110
+ throw err;
111
+ }
112
+ // Never allow callers to smuggle calibrated claims without id via alias fields
113
+ if ('probability' in (partial || {}) || 'probability_true' in (partial || {})) {
114
+ if (!answer.calibration_id && answer.probabilities == null) {
115
+ // Accept probability_true only when calibration_id present; otherwise map to support
116
+ if (partial.probability_true != null && answer.support == null) {
117
+ answer.support = clamp01(Number(partial.probability_true));
118
+ answer.warnings = [
119
+ ...answer.warnings,
120
+ 'probability_true_mapped_to_support_without_calibration',
121
+ ];
122
+ }
123
+ }
124
+ }
125
+ return answer;
126
+ }