@inneranimalmedia/agentsam-sdk 2.4.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/AGENTSAM.md +55 -0
  2. package/README.md +17 -12
  3. package/bin/agentsam +2 -0
  4. package/docs/AGENTSAM_ASTRA_OPENAI_INTEGRATION.md +1363 -0
  5. package/docs/AGENTSAM_WORKBENCH.md +30 -0
  6. package/docs/AUTH_IDENTITY_CONTRACT.md +52 -0
  7. package/docs/CAPABILITIES.md +5 -3
  8. package/docs/CLI_SHELL.md +167 -80
  9. package/docs/CMS_STUDIO.md +101 -0
  10. package/docs/CONTEXT.md +170 -0
  11. package/docs/LOCAL_OLLAMA.md +58 -0
  12. package/docs/MERKLE.md +19 -19
  13. package/docs/PORTABLE_CONTEXT.md +4 -3
  14. package/docs/PROJECT_CONFIG.md +72 -0
  15. package/docs/RELEASES.md +35 -2
  16. package/docs/REPOSITORY_INTELLIGENCE.md +1 -1
  17. package/docs/REPOSITORY_KNOWLEDGE.md +114 -0
  18. package/docs/SDK_WORKER.md +86 -0
  19. package/docs/SECURITY.md +59 -22
  20. package/docs/client-cms-editor.md +15 -0
  21. package/docs/local-studio/WORKMODE_DONOR_NOTES.md +485 -0
  22. package/package.json +35 -10
  23. package/packages/connectors/cloudflare/package.json +10 -0
  24. package/packages/connectors/cloudflare/src/index.js +127 -0
  25. package/packages/connectors/cloudflare/src/owner.js +76 -0
  26. package/packages/connectors/cloudflare/src/routes.js +223 -0
  27. package/packages/connectors/cloudflare/src/vault.js +80 -0
  28. package/packages/connectors/cloudflare/tests/connector.test.mjs +44 -0
  29. package/packages/identity/package.json +10 -1
  30. package/packages/identity/src/contracts/auth-config.js +109 -0
  31. package/packages/identity/src/index.js +1 -0
  32. package/packages/identity/src/oauth/README.md +2 -2
  33. package/packages/identity/src/oauth/credentials.js +11 -4
  34. package/packages/identity/src/oauth/iam-platform.js +3 -3
  35. package/packages/identity/src/providers/iam/index.js +7 -7
  36. package/packages/identity/src/providers/iam/oauth.js +6 -4
  37. package/packages/identity/src/providers/iam/profile.js +5 -5
  38. package/packages/identity/tests/auth-config.test.mjs +61 -0
  39. package/packages/identity/tests/oauth-credentials.test.mjs +13 -2
  40. package/protocol/FILEMETA_V1.md +95 -0
  41. package/protocol/INSPECT_VIEWS_V1.md +40 -0
  42. package/protocol/MERKLE_PERSISTENCE_V1.md +50 -0
  43. package/protocol/MERKLE_V1.md +3 -1
  44. package/protocol/README.md +1 -0
  45. package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +19 -0
  46. package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +13 -0
  47. package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +19 -0
  48. package/protocol/capabilities/manifest.json +79 -3
  49. package/protocol/capabilities/repository-snapshot.schema.json +1 -0
  50. package/protocol/context/context-budget.schema.json +36 -0
  51. package/protocol/context/context-item.schema.json +19 -0
  52. package/protocol/context/resolved-context-pack.schema.json +47 -0
  53. package/protocol/context/result-policy.schema.json +17 -0
  54. package/protocol/knowledge/context-pack.schema.json +33 -0
  55. package/protocol/knowledge/retrieval-query.schema.json +68 -13
  56. package/protocol/models/README.md +373 -0
  57. package/protocol/models/model-inventory-v2.schema.json +212 -0
  58. package/python/README.md +15 -7
  59. package/python/agentsam_sdk/cli.py +0 -21
  60. package/python/agentsam_sdk/tui/README.md +17 -12
  61. package/python/agentsam_sdk/tui/bootstrap.py +2 -2
  62. package/python/agentsam_sdk/tui/demo.py +25 -10
  63. package/python/agentsam_sdk/tui/onboarding.py +208 -0
  64. package/python/tests/test_tui_cli.py +7 -6
  65. package/skills/README.md +21 -0
  66. package/skills/agentsam-app-fundamentals/SKILL.md +165 -0
  67. package/skills/agentsam-app-fundamentals/references/graphs-contracts-ast-merkle.md +89 -0
  68. package/skills/agentsam-app-fundamentals/references/trust-credentials-and-destinations.md +99 -0
  69. package/skills/agentsam-cloudflare-workers/SKILL.md +53 -0
  70. package/skills/agentsam-cloudflare-workers/references/cpu-profiling.md +16 -0
  71. package/skills/agentsam-cloudflare-workers/references/errors-and-observability.md +29 -0
  72. package/skills/agentsam-cloudflare-workers/references/wrangler-native-map.md +28 -0
  73. package/skills/agentsam-jr-dev/SKILL.md +232 -0
  74. package/skills/agentsam-jr-dev/references/real-application-logic.md +156 -0
  75. package/skills/agentsam-jr-dev/references/web-application-fundamentals.md +240 -0
  76. package/skills/agentsam-progression-guard/SKILL.md +197 -0
  77. package/skills/agentsam-progression-guard/references/checkpoint-chain.md +111 -0
  78. package/skills/agentsam-progression-guard/references/hooks-operational-io.md +96 -0
  79. package/skills/catalog.json +71 -0
  80. package/src/agent/capability-adapter.js +25 -13
  81. package/src/agent/index.js +1 -0
  82. package/src/agent/responses-runner.js +325 -0
  83. package/src/capabilities/index.js +7 -0
  84. package/src/capabilities/repository-snapshot-view.js +238 -0
  85. package/src/capabilities/repository-snapshot.js +28 -14
  86. package/src/cli.js +198 -76
  87. package/src/cloudflare/cpu-profile.js +115 -0
  88. package/src/cloudflare/index.js +14 -0
  89. package/src/cloudflare/wrangler.js +132 -0
  90. package/src/commands/account-auth.js +47 -0
  91. package/src/commands/cloudflare.js +58 -0
  92. package/src/commands/connections.js +93 -0
  93. package/src/commands/context-economics.js +114 -0
  94. package/src/commands/context.js +14 -2
  95. package/src/commands/db.js +4 -7
  96. package/src/commands/deploy.js +54 -32
  97. package/src/commands/eval.js +63 -0
  98. package/src/commands/interactive.js +18 -0
  99. package/src/commands/knowledge.js +27 -4
  100. package/src/commands/merkle-persist.js +118 -0
  101. package/src/commands/merkle.js +32 -17
  102. package/src/commands/models.js +152 -0
  103. package/src/commands/ollama.js +259 -0
  104. package/src/commands/preferences.js +144 -0
  105. package/src/commands/product.js +86 -16
  106. package/src/commands/resume.js +67 -0
  107. package/src/commands/security.js +8 -6
  108. package/src/commands/shell.js +405 -100
  109. package/src/commands/skills.js +66 -0
  110. package/src/commands/start-local.js +1 -1
  111. package/src/commands/tunnel.js +5 -4
  112. package/src/commands/whoami.js +86 -0
  113. package/src/context/budget.js +120 -0
  114. package/src/context/compact.js +28 -0
  115. package/src/context/index.js +7 -0
  116. package/src/context/rehydrate.js +35 -0
  117. package/src/context/resolve.js +116 -0
  118. package/src/context/result-policy.js +66 -0
  119. package/src/errors/diagnostic.js +160 -0
  120. package/src/errors/index.js +9 -0
  121. package/src/eval/context.js +191 -0
  122. package/src/eval/index.js +1 -0
  123. package/src/index.js +70 -0
  124. package/src/indexing/execution-boundary.js +144 -0
  125. package/src/indexing/index.js +8 -0
  126. package/src/indexing/provider.js +41 -0
  127. package/src/knowledge/config.js +2 -2
  128. package/src/knowledge/context-pack.js +12 -1
  129. package/src/knowledge/contracts.js +9 -4
  130. package/src/knowledge/engine.js +7 -3
  131. package/src/knowledge/service/server.js +1 -1
  132. package/src/lib/account-session.js +98 -0
  133. package/src/lib/agent-instructions.js +73 -0
  134. package/src/lib/auth.js +4 -0
  135. package/src/lib/bridge-client.js +7 -5
  136. package/src/lib/cli-preferences.js +78 -0
  137. package/src/lib/core-client.js +8 -8
  138. package/src/lib/deploy/git-guard.js +69 -0
  139. package/src/lib/deploy/health.js +57 -0
  140. package/src/lib/deploy/local-studio.js +283 -0
  141. package/src/lib/deploy/secret-scan.js +65 -0
  142. package/src/lib/deploy-receipt/index.js +5 -2
  143. package/src/lib/detect-context.js +6 -5
  144. package/src/lib/execution-approvals.js +59 -0
  145. package/src/lib/identity-scaffold.js +1 -1
  146. package/src/lib/local-scaffold.js +35 -33
  147. package/src/lib/local-sessions.js +127 -0
  148. package/src/lib/local-status.js +9 -17
  149. package/src/lib/merkle/cloudflare-persistence.js +321 -0
  150. package/src/lib/merkle/filemeta.js +43 -0
  151. package/src/lib/merkle/git-ignore.js +24 -0
  152. package/src/lib/merkle/hash.js +1 -0
  153. package/src/lib/merkle/index.js +22 -0
  154. package/src/lib/merkle/persistence.js +72 -0
  155. package/src/lib/merkle/semantic.js +359 -0
  156. package/src/lib/merkle/snapshot.js +9 -3
  157. package/src/lib/merkle/tree.js +11 -6
  158. package/src/lib/project-config.js +227 -0
  159. package/src/lib/project-rules.js +68 -0
  160. package/src/lib/provider-credentials.js +83 -0
  161. package/src/lib/save-sdk-token.js +1 -1
  162. package/src/lib/scaffold/templates/worker-api/index.js +101 -20
  163. package/src/lib/scaffold/wizards/worker-api.js +27 -11
  164. package/src/lib/slash-commands.js +22 -15
  165. package/src/lib/tools.js +11 -5
  166. package/src/models/catalog.js +135 -0
  167. package/src/models/index.js +7 -0
  168. package/src/providers/index.js +5 -0
  169. package/src/providers/openai-responses.js +275 -0
  170. package/src/security/index.js +1 -0
  171. package/src/security/inventory.js +4 -1
  172. package/src/security/process.js +35 -9
  173. package/src/security/render.js +27 -5
  174. package/src/security/scan.js +24 -9
  175. package/src/security/trust-boundary.js +24 -0
  176. package/src/skills/index.js +64 -0
  177. package/src/telemetry/contracts.js +203 -0
  178. package/src/telemetry/events.js +48 -0
  179. package/src/telemetry/index.js +8 -0
  180. package/src/tools/hydrate.js +35 -0
  181. package/src/tools/index.js +2 -0
  182. package/src/tools/search.js +70 -0
  183. package/src/ui/ansi.js +1 -1
  184. package/src/ui/boot.js +54 -0
  185. package/src/ui/merkle/render.js +1 -0
  186. package/src/ui/runtime-activity.js +192 -0
  187. package/src/ui/theme.js +19 -18
  188. package/test/account-session.test.mjs +36 -0
  189. package/test/app-building-skills.test.mjs +61 -0
  190. package/test/apps-scaffold-contract.test.mjs +56 -0
  191. package/test/capabilities.test.mjs +53 -4
  192. package/test/cli-preferences.test.mjs +46 -0
  193. package/test/cloudflare-connector.test.mjs +96 -0
  194. package/test/cloudflare-runtime.test.mjs +75 -0
  195. package/test/context.test.mjs +144 -0
  196. package/test/deploy-health-scan.test.mjs +67 -0
  197. package/test/error-diagnostics.test.mjs +59 -0
  198. package/test/eval-context.test.mjs +37 -0
  199. package/test/execution-approvals.test.mjs +27 -0
  200. package/test/indexing-provider.test.mjs +29 -0
  201. package/test/jr-dev-skill.test.mjs +26 -0
  202. package/test/knowledge-context-pack.test.mjs +19 -0
  203. package/test/knowledge.test.mjs +1 -1
  204. package/test/local-sessions.test.mjs +42 -0
  205. package/test/local-studio-deploy.test.mjs +83 -0
  206. package/test/merkle-persistence.test.mjs +91 -0
  207. package/test/merkle.test.mjs +77 -3
  208. package/test/model-catalog.test.mjs +43 -0
  209. package/test/models.test.mjs +51 -0
  210. package/test/npm10-lock.test.mjs +29 -0
  211. package/test/ollama.test.mjs +94 -0
  212. package/test/openai-responses.test.mjs +95 -0
  213. package/test/project-config.test.mjs +81 -0
  214. package/test/project-rules.test.mjs +44 -0
  215. package/test/provider-credentials.test.mjs +52 -0
  216. package/test/rehydrate.test.mjs +25 -0
  217. package/test/release-hygiene.test.mjs +34 -0
  218. package/test/repository-snapshot-view.test.mjs +112 -0
  219. package/test/responses-runner.test.mjs +148 -0
  220. package/test/runtime-activity.test.mjs +98 -0
  221. package/test/sdk-worker-contract.test.mjs +68 -0
  222. package/test/security.test.mjs +46 -0
  223. package/test/shell.test.mjs +55 -21
  224. package/test/skills.test.mjs +22 -0
  225. package/test/smoke.mjs +5 -2
  226. package/test/telemetry.test.mjs +79 -0
  227. package/test/theme-portability.test.mjs +14 -0
  228. package/test/tools-search.test.mjs +40 -0
  229. package/test/whoami-resume.test.mjs +56 -0
  230. package/examples/agentsam-tui-ansi.mjs +0 -149
  231. package/src/commands/tui.js +0 -120
  232. package/src/ui/splash-xterm.js +0 -290
  233. package/src/ui/splash.js +0 -426
@@ -0,0 +1,283 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import crypto from 'node:crypto';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { assertProductionDeployAllowed, inspectDeployGit } from './git-guard.js';
6
+ import { assertNoDeploySecrets } from './secret-scan.js';
7
+ import { parseWranglerVersionId, probeDeployHealth, resolveHealthOrigin } from './health.js';
8
+
9
+ export const LOCAL_STUDIO_REL = 'apps/local-studio';
10
+ export const WRANGLER_CONFIG_REL = 'backend/wrangler.jsonc';
11
+ export const DEPLOY_INPUT_GLOBS = Object.freeze([
12
+ 'apps/local-studio',
13
+ 'packages/agentsam-contracts',
14
+ 'packages/agentsam-workbench',
15
+ 'packages/connectors/cloudflare',
16
+ ]);
17
+
18
+ export function findRepoRoot(start = process.cwd()) {
19
+ let dir = path.resolve(start);
20
+ for (;;) {
21
+ if (fs.existsSync(path.join(dir, 'apps/local-studio/backend/wrangler.jsonc'))) return dir;
22
+ if (fs.existsSync(path.join(dir, 'backend/wrangler.jsonc')) && path.basename(dir) === 'local-studio') {
23
+ return path.dirname(path.dirname(dir));
24
+ }
25
+ const parent = path.dirname(dir);
26
+ if (parent === dir) return start;
27
+ dir = parent;
28
+ }
29
+ }
30
+
31
+ export function resolveLocalStudioDeployable(cwd = process.cwd()) {
32
+ const repoRoot = findRepoRoot(cwd);
33
+ const appRoot = path.join(repoRoot, LOCAL_STUDIO_REL);
34
+ const wranglerConfig = path.join(appRoot, WRANGLER_CONFIG_REL);
35
+ if (!fs.existsSync(wranglerConfig)) {
36
+ throw new Error(`wrangler config missing: ${wranglerConfig}`);
37
+ }
38
+ return {
39
+ repoRoot,
40
+ appRoot,
41
+ wranglerConfig,
42
+ wranglerArgs: ['deploy', '-c', WRANGLER_CONFIG_REL],
43
+ provider: 'cloudflare',
44
+ app: 'local-studio',
45
+ };
46
+ }
47
+
48
+ export function loadOptionalCloudflareEnv(appRoot) {
49
+ const file = path.join(appRoot, '.env.cloudflare');
50
+ if (!fs.existsSync(file)) return { loaded: false, vars: {} };
51
+ const vars = {};
52
+ for (const line of fs.readFileSync(file, 'utf8').split(/\r?\n/)) {
53
+ const trimmed = line.trim();
54
+ if (!trimmed || trimmed.startsWith('#')) continue;
55
+ const eq = trimmed.indexOf('=');
56
+ if (eq < 1) continue;
57
+ const key = trimmed.slice(0, eq).trim();
58
+ let value = trimmed.slice(eq + 1).trim();
59
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
60
+ value = value.slice(1, -1);
61
+ }
62
+ vars[key] = value;
63
+ }
64
+ return { loaded: true, vars };
65
+ }
66
+
67
+ function hashFile(file) {
68
+ const h = crypto.createHash('sha256');
69
+ h.update(fs.readFileSync(file));
70
+ return h.digest('hex');
71
+ }
72
+
73
+ function walkFiles(dir, acc = []) {
74
+ if (!fs.existsSync(dir)) return acc;
75
+ for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
76
+ if (ent.name === 'node_modules' || ent.name === '.output' || ent.name === '.wrangler' || ent.name === '.git') continue;
77
+ const full = path.join(dir, ent.name);
78
+ if (ent.isDirectory()) walkFiles(full, acc);
79
+ else acc.push(full);
80
+ }
81
+ return acc;
82
+ }
83
+
84
+ export function computeDeployFingerprint(repoRoot) {
85
+ const h = crypto.createHash('sha256');
86
+ const files = [];
87
+ for (const rel of DEPLOY_INPUT_GLOBS) {
88
+ walkFiles(path.join(repoRoot, rel), files);
89
+ }
90
+ files.sort();
91
+ for (const file of files) {
92
+ const rel = path.relative(repoRoot, file).split(path.sep).join('/');
93
+ h.update(rel);
94
+ h.update('\0');
95
+ h.update(hashFile(file));
96
+ h.update('\n');
97
+ }
98
+ return h.digest('hex');
99
+ }
100
+
101
+ export function readBaselineFingerprint(appRoot) {
102
+ const receipt = path.join(appRoot, '.agentsam/deploy-merkle/latest.receipt.json');
103
+ if (!fs.existsSync(receipt)) return null;
104
+ try {
105
+ const json = JSON.parse(fs.readFileSync(receipt, 'utf8'));
106
+ return json.deployProjectionHash || json.fingerprint || null;
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ export function writeDeployReceipt(appRoot, receipt) {
113
+ const dir = path.join(appRoot, '.agentsam/deploy-merkle');
114
+ fs.mkdirSync(dir, { recursive: true });
115
+ const file = path.join(dir, receipt.promoted ? 'latest.receipt.json' : 'pending.receipt.json');
116
+ fs.writeFileSync(file, JSON.stringify(receipt, null, 2) + '\n');
117
+ return file;
118
+ }
119
+
120
+ export function wranglerDeployCommand(target, { dryRun = false } = {}) {
121
+ const args = ['wrangler', ...target.wranglerArgs];
122
+ if (dryRun) args.push('--dry-run');
123
+ return { cwd: target.appRoot, args, bin: 'npx' };
124
+ }
125
+
126
+ export function isLocalStudioCheckout(cwd = process.cwd()) {
127
+ try {
128
+ resolveLocalStudioDeployable(cwd);
129
+ return true;
130
+ } catch {
131
+ return false;
132
+ }
133
+ }
134
+
135
+ export async function runLocalStudioDeploy({
136
+ cwd = process.cwd(),
137
+ dryRun = false,
138
+ planOnly = false,
139
+ skipBuild = false,
140
+ execute = true,
141
+ skipSecretScan = false,
142
+ probeHealth = null,
143
+ } = {}) {
144
+ const target = resolveLocalStudioDeployable(cwd);
145
+ if (path.resolve(cwd) === target.repoRoot && !target.wranglerConfig.endsWith(path.join('backend', 'wrangler.jsonc')) && !target.wranglerConfig.endsWith('backend/wrangler.jsonc')) {
146
+ throw new Error('refusing generic repo-root wrangler deploy');
147
+ }
148
+ const envFile = loadOptionalCloudflareEnv(target.appRoot);
149
+ const fingerprint = computeDeployFingerprint(target.repoRoot);
150
+ const baseline = readBaselineFingerprint(target.appRoot);
151
+ const gitInfo = inspectDeployGit(target.repoRoot);
152
+ const wranglerText = fs.readFileSync(target.wranglerConfig, 'utf8');
153
+ const healthOrigin = resolveHealthOrigin({ env: { ...process.env, ...envFile.vars }, wranglerConfigText: wranglerText });
154
+ const plan = {
155
+ provider: 'cloudflare',
156
+ app: 'local-studio',
157
+ wranglerConfig: path.relative(target.repoRoot, target.wranglerConfig).split(path.sep).join('/'),
158
+ wranglerArgs: target.wranglerArgs,
159
+ cwd: path.relative(target.repoRoot, target.appRoot).split(path.sep).join('/') || '.',
160
+ envLoaded: envFile.loaded,
161
+ fingerprint,
162
+ baseline,
163
+ skip: Boolean(baseline && baseline === fingerprint && !dryRun && !planOnly),
164
+ genericRootDeploy: false,
165
+ productionGitRequired: Boolean(execute && !dryRun && !planOnly),
166
+ healthOrigin,
167
+ git: {
168
+ branch: gitInfo.branch,
169
+ head: gitInfo.head,
170
+ originMain: gitInfo.originMain,
171
+ equal: Boolean(gitInfo.head && gitInfo.head === gitInfo.originMain),
172
+ },
173
+ };
174
+ if (!skipSecretScan) {
175
+ plan.secretScan = assertNoDeploySecrets([
176
+ target.wranglerConfig,
177
+ path.join(target.appRoot, '.env.cloudflare.example'),
178
+ path.join(target.appRoot, 'backend/worker/index.js'),
179
+ path.join(target.repoRoot, 'packages/connectors/cloudflare/src/index.js'),
180
+ ]);
181
+ }
182
+ if (plan.skip) {
183
+ const receipt = {
184
+ ok: true,
185
+ skipped: true,
186
+ reason: 'unchanged_deploy_fingerprint',
187
+ deployProjectionHash: fingerprint,
188
+ promoted: false,
189
+ git: plan.git,
190
+ wranglerConfig: plan.wranglerConfig,
191
+ hostname: healthOrigin.replace(/^https?:\/\//, ''),
192
+ };
193
+ writeDeployReceipt(target.appRoot, receipt);
194
+ return { target, plan, receipt };
195
+ }
196
+ if (!execute || planOnly) {
197
+ return {
198
+ target,
199
+ plan,
200
+ receipt: {
201
+ ok: true,
202
+ skipped: false,
203
+ plan: true,
204
+ dryRun,
205
+ deployProjectionHash: fingerprint,
206
+ promoted: false,
207
+ git: plan.git,
208
+ wranglerConfig: plan.wranglerConfig,
209
+ hostname: healthOrigin.replace(/^https?:\/\//, ''),
210
+ },
211
+ };
212
+ }
213
+
214
+ if (!dryRun) {
215
+ plan.git = assertProductionDeployAllowed(target.repoRoot);
216
+ plan.git.equal = plan.git.head === plan.git.originMain;
217
+ }
218
+
219
+ const env = { ...process.env, ...envFile.vars };
220
+ const lock = spawnSync('npm', ['run', 'verify:npm10-lock'], { cwd: target.appRoot, env, encoding: 'utf8' });
221
+ if (lock.status !== 0) throw new Error(lock.stderr || lock.stdout || 'verify:npm10-lock failed');
222
+ if (!skipBuild) {
223
+ const build = spawnSync('npm', ['run', 'build'], { cwd: target.appRoot, env, encoding: 'utf8' });
224
+ if (build.status !== 0) throw new Error(build.stderr || build.stdout || 'build failed');
225
+ const verify = spawnSync('npm', ['run', 'cf:verify-output'], { cwd: target.appRoot, env, encoding: 'utf8' });
226
+ if (verify.status !== 0) throw new Error(verify.stderr || verify.stdout || 'cf:verify-output failed');
227
+ }
228
+ const cmd = wranglerDeployCommand(target, { dryRun });
229
+ const deployed = spawnSync(cmd.bin, cmd.args, { cwd: cmd.cwd, env, encoding: 'utf8' });
230
+ const wranglerOut = `${deployed.stdout || ''}\n${deployed.stderr || ''}`;
231
+ const workerVersion = parseWranglerVersionId(wranglerOut);
232
+ if (deployed.status !== 0) {
233
+ const receipt = {
234
+ ok: false,
235
+ skipped: false,
236
+ deployProjectionHash: fingerprint,
237
+ promoted: false,
238
+ git: plan.git,
239
+ workerVersion,
240
+ error: wranglerOut,
241
+ };
242
+ writeDeployReceipt(target.appRoot, receipt);
243
+ throw new Error(receipt.error || 'wrangler deploy failed');
244
+ }
245
+ let health = null;
246
+ if (!dryRun) {
247
+ const probe = probeHealth || probeDeployHealth;
248
+ health = await probe(healthOrigin);
249
+ if (!health.ok) {
250
+ const receipt = {
251
+ ok: false,
252
+ skipped: false,
253
+ deployProjectionHash: fingerprint,
254
+ promoted: false,
255
+ git: plan.git,
256
+ workerVersion,
257
+ hostname: healthOrigin.replace(/^https?:\/\//, ''),
258
+ health,
259
+ error: 'postdeploy_health_failed',
260
+ };
261
+ writeDeployReceipt(target.appRoot, receipt);
262
+ throw new Error('postdeploy_health_failed');
263
+ }
264
+ }
265
+ const receipt = {
266
+ ok: true,
267
+ skipped: false,
268
+ dryRun,
269
+ deployProjectionHash: fingerprint,
270
+ promoted: !dryRun,
271
+ provider: 'cloudflare',
272
+ app: 'local-studio',
273
+ wranglerConfig: plan.wranglerConfig,
274
+ git: plan.git,
275
+ worker: 'agentsam-sdk',
276
+ workerVersion,
277
+ hostname: healthOrigin.replace(/^https?:\/\//, ''),
278
+ health,
279
+ originMainEqual: Boolean(plan.git?.head && plan.git.head === plan.git.originMain),
280
+ };
281
+ writeDeployReceipt(target.appRoot, receipt);
282
+ return { target, plan, receipt };
283
+ }
@@ -0,0 +1,65 @@
1
+ /** Scan deployable files for live secrets. Fixtures and .example placeholders are allowed. */
2
+ import fs from 'node:fs';
3
+
4
+ const FIXTURES = new Set([
5
+ 'sillynotreal',
6
+ 'sillynotreal-secret',
7
+ 'CHANGEME',
8
+ 'replace-me',
9
+ 'your-token-here',
10
+ '***',
11
+ ]);
12
+
13
+ const LIVE_PATTERNS = [
14
+ { re: /\bsk-[A-Za-z0-9]{20,}\b/g, label: 'openai-like' },
15
+ { re: /\bxai-[A-Za-z0-9]{20,}\b/g, label: 'xai-like' },
16
+ { re: /\bAKIA[0-9A-Z]{16}\b/g, label: 'aws-like' },
17
+ ];
18
+
19
+ const ASSIGN_RE = /((?:CLOUDFLARE_OAUTH_CLIENT_SECRET|CLOUDFLARE_API_TOKEN|IAM_CLIENT_SECRET|VAULT_MASTER_KEY)\s*=\s*)([^\s#]+)/g;
20
+
21
+ function isExample(filename = '') {
22
+ return filename.endsWith('.example') || filename.includes('.env.cloudflare.example');
23
+ }
24
+
25
+ export function scanTextForSecrets(text, { filename = '' } = {}) {
26
+ const findings = [];
27
+ const src = String(text || '');
28
+ for (const { re, label } of LIVE_PATTERNS) {
29
+ re.lastIndex = 0;
30
+ let m;
31
+ while ((m = re.exec(src))) {
32
+ if (FIXTURES.has(m[0])) continue;
33
+ findings.push({ filename, label, preview: `${m[0].slice(0, 5)}…` });
34
+ }
35
+ }
36
+ if (isExample(filename)) return findings;
37
+ ASSIGN_RE.lastIndex = 0;
38
+ let m;
39
+ while ((m = ASSIGN_RE.exec(src))) {
40
+ const val = m[2].replace(/^[\'"]|[\'"]$/g, '');
41
+ if (!val || FIXTURES.has(val) || val.includes('your-') || val.length < 16) continue;
42
+ findings.push({ filename, label: 'assigned-secret', preview: `${m[1].trim()}[redacted]` });
43
+ }
44
+ return findings;
45
+ }
46
+
47
+ export function scanFilesForSecrets(files = []) {
48
+ const findings = [];
49
+ for (const file of files) {
50
+ if (!file || !fs.existsSync(file) || !fs.statSync(file).isFile()) continue;
51
+ findings.push(...scanTextForSecrets(fs.readFileSync(file, 'utf8'), { filename: file }));
52
+ }
53
+ return findings;
54
+ }
55
+
56
+ export function assertNoDeploySecrets(files = []) {
57
+ const findings = scanFilesForSecrets(files);
58
+ if (findings.length) {
59
+ const err = new Error('secret_scan_failed: ' + findings.map((f) => f.label).join(', '));
60
+ err.code = 'secret_scan_failed';
61
+ err.findings = findings;
62
+ throw err;
63
+ }
64
+ return { ok: true, findings: [] };
65
+ }
@@ -146,7 +146,7 @@ export async function captureDeployReceipt({
146
146
  }
147
147
  }
148
148
 
149
- const tree = await buildMerkleTree(rootPath, { policy });
149
+ const tree = await buildMerkleTree(rootPath, { policy, semantic: true });
150
150
  await atomicJson(state.pendingSnapshot, tree);
151
151
 
152
152
  let diff = null;
@@ -163,7 +163,7 @@ export async function captureDeployReceipt({
163
163
  const receipt = {
164
164
  format: 'agentsam-deploy-merkle',
165
165
  version: 1,
166
- engine: 'agentsam-merkle-v1',
166
+ engine: tree.semantic ? 'agentsam-merkle-v1+agentsam-filemeta-v1' : 'agentsam-merkle-v1',
167
167
  status: 'captured',
168
168
  project: project || path.basename(rootPath),
169
169
  repository: gitInfo.repository || null,
@@ -171,6 +171,9 @@ export async function captureDeployReceipt({
171
171
  git_branch: gitInfo.git_branch || null,
172
172
  captured_at: new Date().toISOString(),
173
173
  root_hash: tree.rootHash,
174
+ policy_hash: tree.policyHash || null,
175
+ metadata_root: tree.semantic?.rootHash || null,
176
+ classifier: tree.semantic?.classifier || null,
174
177
  previous_root_hash: baseline?.rootHash || null,
175
178
  has_baseline: Boolean(baseline),
176
179
  baseline_source: resolvedBaselineSource,
@@ -5,6 +5,7 @@
5
5
  import { execFile } from 'child_process';
6
6
  import { promisify } from 'util';
7
7
  import { coreBaseUrl } from './core-client.js';
8
+ import { resolveAccountSdkKey } from './account-session.js';
8
9
 
9
10
  const execFileAsync = promisify(execFile);
10
11
 
@@ -220,22 +221,22 @@ async function detectIam(explicitToken = '') {
220
221
  });
221
222
  }
222
223
 
223
- const sdkToken = explicitToken || process.env.AGENTSAM_SDK_TOKEN || '';
224
+ const sdkToken = resolveAccountSdkKey({ env: process.env, explicit: explicitToken }).value;
224
225
  if (sdkToken.trim()) {
225
226
  const probe = await probeSdkBearer(sdkToken);
226
227
  if (probe.valid) {
227
228
  return {
228
- source: 'sdk-token',
229
+ source: 'sdk-key',
229
230
  ready: true,
230
- detail: `AGENTSAM_SDK_TOKEN · user ${probe.user_id || '?'}`,
231
+ detail: `AGENTSAM_SDK_KEY · user ${probe.user_id || '?'}`,
231
232
  probe,
232
233
  aux,
233
234
  };
234
235
  }
235
236
  return {
236
- source: 'sdk-token',
237
+ source: 'sdk-key',
237
238
  ready: false,
238
- detail: `AGENTSAM_SDK_TOKEN invalid (${probe.error}) → will open browser`,
239
+ detail: `AGENTSAM_SDK_KEY invalid (${probe.error}) → will open browser`,
239
240
  probe,
240
241
  aux,
241
242
  };
@@ -0,0 +1,59 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ const SCHEMA = 'agentsam-execution-approvals-v1';
6
+
7
+ function clean(value) { return value == null ? '' : String(value).trim(); }
8
+ function homeDirectory(options = {}) { return path.resolve(clean(options.home) || clean(options.env?.HOME) || clean(options.env?.USERPROFILE) || os.homedir()); }
9
+ function approvalsPath(options = {}) { return path.join(homeDirectory(options), '.agentsam', 'execution-approvals.json'); }
10
+
11
+ function readStore(options = {}) {
12
+ const filename = approvalsPath(options);
13
+ if (!fs.existsSync(filename)) return { schema_version: SCHEMA, approvals: [] };
14
+ try {
15
+ const parsed = JSON.parse(fs.readFileSync(filename, 'utf8'));
16
+ if (parsed?.schema_version !== SCHEMA || !Array.isArray(parsed.approvals)) return { schema_version: SCHEMA, approvals: [] };
17
+ return parsed;
18
+ } catch { return { schema_version: SCHEMA, approvals: [] }; }
19
+ }
20
+
21
+ function writeStore(store, options = {}) {
22
+ const filename = approvalsPath(options);
23
+ fs.mkdirSync(path.dirname(filename), { recursive: true, mode: 0o700 });
24
+ const temp = `${filename}.${process.pid}.tmp`;
25
+ fs.writeFileSync(temp, `${JSON.stringify(store, null, 2)}\n`, { mode: 0o600 });
26
+ if (process.platform !== 'win32') {
27
+ try { fs.chmodSync(temp, 0o600); } catch { /* best effort */ }
28
+ }
29
+ fs.renameSync(temp, filename);
30
+ }
31
+
32
+ export function toolApprovalKey(capabilityId, input = {}) {
33
+ const id = clean(capabilityId);
34
+ const command = clean(input?.command);
35
+ if (id === 'cloudflare.wrangler.native' && /^[A-Za-z0-9._-]+$/.test(command)) return `${id}:${command}`;
36
+ return id;
37
+ }
38
+
39
+ export function isExecutionApproved({ cwd, key }, options = {}) {
40
+ const root = path.resolve(clean(cwd) || process.cwd());
41
+ const target = clean(key);
42
+ if (!target) return false;
43
+ return readStore(options).approvals.some((row) => row?.cwd === root && row?.key === target);
44
+ }
45
+
46
+ export function grantExecutionApproval({ cwd, key, label = '' }, options = {}) {
47
+ const root = path.resolve(clean(cwd) || process.cwd());
48
+ const target = clean(key);
49
+ if (!target) throw new Error('execution_approval_key_required');
50
+ const store = readStore(options);
51
+ const approvals = store.approvals.filter((row) => !(row?.cwd === root && row?.key === target));
52
+ approvals.push({ cwd: root, key: target, label: clean(label) || target, created_at: new Date().toISOString() });
53
+ writeStore({ schema_version: SCHEMA, approvals }, options);
54
+ return { cwd: root, key: target, persisted: true };
55
+ }
56
+
57
+ export function listExecutionApprovals(options = {}) {
58
+ return readStore(options).approvals.map((row) => ({ ...row }));
59
+ }
@@ -75,7 +75,7 @@ migrations_dir = "migrations"
75
75
  files['.env.example'] = `# Minted at install/build — secrets via wrangler secret put (never commit)
76
76
  # IAM_CLIENT_ID= # plaintext var (public by OAuth design)
77
77
  # IAM_CLIENT_SECRET= # wrangler secret put only
78
- # IAM_OAUTH_ISSUER=https://inneranimalmedia.com
78
+ # IAM_ORIGIN=https://inneranimalmedia.com
79
79
 
80
80
  # Developer BYOK — takes /api/oauth/{google|github}/start when set
81
81
  # GOOGLE_CLIENT_ID=
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  import pkg from '../../package.json' with { type: 'json' };
7
+ import { createProjectManifest } from './project-config.js';
8
+ import { defaultProjectRules } from './project-rules.js';
7
9
 
8
10
  const LANE_KEYS = {
9
11
  '1': 'fullstack',
@@ -139,37 +141,24 @@ export function buildLocalScaffoldFiles({
139
141
  const migration = migrationSql(laneKey);
140
142
 
141
143
  return [
142
- {
143
- path: 'agentsam.config.js',
144
- content: `export default {
145
- project: '${projectName}',
146
- lane: '${laneKey}',
147
- agent: '${agent}',
148
- runTarget: '${runTarget}',
149
- api: { baseUrl: '/api/agentsam' },
150
- };
151
- `,
152
- },
153
144
  {
154
145
  path: '.agentsam/config.json',
155
146
  content: `${JSON.stringify(
156
- {
157
- project: projectName,
158
- lane: laneKey,
159
- agent,
160
- run_target: 'local',
161
- deploy_target: runTarget === 'local' ? null : runTarget,
162
- pty_port: 3099,
163
- dev_port: 8787,
164
- db_path: '.agentsam/data/agentsam.sqlite',
165
- db_schema: 'db/schema.sql',
166
- ui: 'terminal',
167
- scaffold_version: sdkVersion,
168
- },
147
+ createProjectManifest({
148
+ projectName,
149
+ preset: laneKey,
150
+ profile: agent,
151
+ runTarget,
152
+ sdkVersion,
153
+ }),
169
154
  null,
170
155
  2,
171
156
  )}\n`,
172
157
  },
158
+ {
159
+ path: '.agentsamrules',
160
+ content: defaultProjectRules(projectName),
161
+ },
173
162
  {
174
163
  path: '.agentsam/start-local.md',
175
164
  content: `# Agent Sam local project
@@ -187,9 +176,10 @@ Useful local surfaces:
187
176
 
188
177
  \`\`\`bash
189
178
  npm run db:status
190
- npm run tui
191
- npm run tui:rich -- --install # optional Rich UI in isolated .agentsam venv
179
+ npx agentsam # enter Agent Sam
192
180
  npx agentsam start-local # PTY on ws://127.0.0.1:3099
181
+ npm run ollama:setup # optional local/free model config
182
+ npm run ollama:status # probe local Ollama + models
193
183
  \`\`\`
194
184
 
195
185
  The local SQLite database lives at \`.agentsam/data/agentsam.sqlite\` and is initialized from \`db/schema.sql\`.
@@ -209,7 +199,12 @@ npx agentsam deploy
209
199
  dist/
210
200
  .wrangler/
211
201
  .agentsam/data/
202
+ .agentsam/cli.json
212
203
  .agentsam/tui-venv/
204
+ .agentsam/knowledge/
205
+ .agentsam/cache/
206
+ .agentsam/merkle/
207
+ .agentsam/merkle.json
213
208
  *.db
214
209
  *.sqlite
215
210
  *.sqlite-*
@@ -231,8 +226,8 @@ dist/
231
226
  'db:init': 'agentsam db init',
232
227
  'db:status': 'agentsam db status',
233
228
  pty: 'agentsam start-local',
234
- tui: 'agentsam tui',
235
- 'tui:rich': 'agentsam tui rich',
229
+ 'ollama:setup': 'agentsam ollama setup',
230
+ 'ollama:status': 'agentsam ollama status',
236
231
  deploy: 'agentsam deploy',
237
232
  },
238
233
  engines: {
@@ -262,6 +257,11 @@ AGENTSAM_LANE=${laneKey}
262
257
  AGENTSAM_AGENT=${agent}
263
258
  AGENTSAM_DB=.agentsam/data/agentsam.sqlite
264
259
  PORT=8787
260
+
261
+ # Optional free/local model runtime. Enable with: agentsam ollama setup
262
+ # OLLAMA_BASE_URL=http://127.0.0.1:11434
263
+ # OLLAMA_MODEL=qwen2.5-coder
264
+ # OLLAMA_EMBED_MODEL=mxbai-embed-large
265
265
  `,
266
266
  },
267
267
  {
@@ -399,13 +399,15 @@ Local state:
399
399
  ## Agent Sam terminal experience
400
400
 
401
401
  \`\`\`bash
402
- npm run tui # zero-dependency ANSI dashboard
403
- npm run tui:rich # Python Rich dashboard if installed
404
- npm run tui:rich -- --install
402
+ npx agentsam # enter the interactive Agent Sam experience
405
403
  npm run pty # local PTY on ws://127.0.0.1:3099
406
404
  npm run db:status
405
+ npm run ollama:setup # optional local/free model config
406
+ npm run ollama:status # local Ollama + model readiness
407
407
  \`\`\`
408
408
 
409
+ Ollama is optional and local-only; it is not deployed as an edge model. Remote AgentSam sessions can reach it by executing the CLI through the existing user-hosted terminal tunnel.
410
+
409
411
  No Worker or cloud account is required for local development.
410
412
 
411
413
  ## Graduate intentionally
@@ -443,9 +445,9 @@ export function buildLocalScaffoldMeta(body, sdkVersion = pkg.version) {
443
445
  'npm run smoke',
444
446
  'npm run status',
445
447
  'npm run dev',
446
- 'npm run tui',
448
+ 'npx agentsam',
447
449
  'npm run db:status',
448
- 'Optional: npm run tui:rich -- --install',
450
+ 'Optional free/local models: npm run ollama:setup',
449
451
  'Optional: npm run pty',
450
452
  'When ready for cloud: npm run deploy',
451
453
  ],