@inneranimalmedia/agentsam-sdk 2.5.0 → 2.6.1

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 (211) hide show
  1. package/AGENTSAM.md +55 -0
  2. package/README.md +12 -8
  3. package/bin/agentsam +2 -0
  4. package/docs/AGENTSAM_ASTRA_OPENAI_INTEGRATION.md +1363 -0
  5. package/docs/CLI_SHELL.md +163 -53
  6. package/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
  7. package/docs/RELEASES.md +16 -7
  8. package/docs/SOURCE_ARCHITECTURE.md +58 -0
  9. package/docs/TEST_TIERS.md +26 -0
  10. package/migrations/runtime/0001_cli_runtime.sql +298 -0
  11. package/package.json +45 -12
  12. package/packages/agentsam-repository/README.md +15 -0
  13. package/packages/agentsam-repository/package.json +25 -0
  14. package/packages/agentsam-repository/src/contracts.js +113 -0
  15. package/packages/agentsam-repository/src/index.js +3 -0
  16. package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
  17. package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
  18. package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
  19. package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
  20. package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
  21. package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
  22. package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
  23. package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
  24. package/packages/connectors/cloudflare/package.json +10 -0
  25. package/packages/connectors/cloudflare/src/index.js +127 -0
  26. package/packages/connectors/cloudflare/src/owner.js +76 -0
  27. package/packages/connectors/cloudflare/src/routes.js +223 -0
  28. package/packages/connectors/cloudflare/src/vault.js +80 -0
  29. package/packages/connectors/cloudflare/tests/connector.test.mjs +44 -0
  30. package/packages/identity/package.json +2 -2
  31. package/packages/identity/src/contracts/auth-config.js +18 -7
  32. package/packages/identity/tests/auth-config.test.mjs +9 -5
  33. package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
  34. package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
  35. package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
  36. package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
  37. package/protocol/README.md +1 -0
  38. package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +19 -0
  39. package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +13 -0
  40. package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +19 -0
  41. package/protocol/capabilities/manifest.json +47 -0
  42. package/protocol/context/context-budget.schema.json +10 -15
  43. package/protocol/context/context-item.schema.json +4 -5
  44. package/protocol/context/resolved-context-pack.schema.json +19 -14
  45. package/protocol/models/README.md +373 -0
  46. package/protocol/models/model-inventory-v2.schema.json +212 -0
  47. package/protocol/repository/repository-contract.schema.json +24 -0
  48. package/protocol/repository/repository-dependency.schema.json +24 -0
  49. package/protocol/repository/repository-identity.schema.json +17 -0
  50. package/protocol/rpc/v1/common.proto +16 -0
  51. package/protocol/rpc/v1/errors.proto +35 -0
  52. package/protocol/rpc/v1/knowledge.proto +77 -0
  53. package/services/knowledge/package-lock.json +333 -0
  54. package/services/knowledge/package.json +5 -1
  55. package/skills/agentsam-cloudflare-workers/SKILL.md +53 -0
  56. package/skills/agentsam-cloudflare-workers/references/cpu-profiling.md +16 -0
  57. package/skills/agentsam-cloudflare-workers/references/errors-and-observability.md +29 -0
  58. package/skills/agentsam-cloudflare-workers/references/wrangler-native-map.md +28 -0
  59. package/skills/catalog.json +18 -0
  60. package/src/agent/capability-adapter.js +25 -13
  61. package/src/agent/index.js +1 -0
  62. package/src/agent/responses-runner.js +353 -0
  63. package/src/capabilities/repository-snapshot.js +3 -3
  64. package/src/cli.js +118 -31
  65. package/src/cloudflare/cpu-profile.js +115 -0
  66. package/src/cloudflare/index.js +14 -0
  67. package/src/cloudflare/wrangler.js +132 -0
  68. package/src/commands/account-auth.js +47 -0
  69. package/src/commands/cloudflare.js +58 -0
  70. package/src/commands/connections.js +93 -0
  71. package/src/commands/context-economics.js +129 -0
  72. package/src/commands/context.js +1 -1
  73. package/src/commands/db.js +20 -3
  74. package/src/commands/deploy.js +39 -3
  75. package/src/commands/env.js +90 -0
  76. package/src/commands/eval.js +63 -0
  77. package/src/commands/interactive.js +2 -5
  78. package/src/commands/knowledge.js +12 -4
  79. package/src/commands/merkle-persist.js +30 -11
  80. package/src/commands/merkle.js +1 -1
  81. package/src/commands/models.js +149 -46
  82. package/src/commands/ollama.js +26 -0
  83. package/src/commands/preferences.js +130 -61
  84. package/src/commands/resume.js +67 -0
  85. package/src/commands/security.js +5 -3
  86. package/src/commands/shell.js +568 -119
  87. package/src/commands/tunnel.js +2 -2
  88. package/src/commands/whoami.js +86 -0
  89. package/src/context/budget.js +68 -6
  90. package/src/context/index.js +3 -1
  91. package/src/context/rehydrate.js +35 -0
  92. package/src/context/resolve.js +44 -12
  93. package/src/errors/contract.js +236 -0
  94. package/src/errors/diagnostic.js +160 -0
  95. package/src/errors/index.js +23 -0
  96. package/src/eval/context.js +191 -0
  97. package/src/eval/index.js +1 -0
  98. package/src/index.js +68 -2
  99. package/src/knowledge/service/auth.js +13 -0
  100. package/src/knowledge/service/grpc-client.js +115 -0
  101. package/src/knowledge/service/grpc-codec.js +237 -0
  102. package/src/knowledge/service/grpc-server.js +83 -0
  103. package/src/knowledge/service/job-engine.js +248 -0
  104. package/src/knowledge/service/server.js +87 -135
  105. package/src/knowledge/source.js +1 -1
  106. package/src/lib/account-session.js +98 -0
  107. package/src/lib/agent-instructions.js +73 -0
  108. package/src/lib/auth.js +4 -0
  109. package/src/lib/cli-preferences.js +55 -24
  110. package/src/lib/deploy/git-guard.js +69 -0
  111. package/src/lib/deploy/health.js +57 -0
  112. package/src/lib/deploy/local-studio.js +283 -0
  113. package/src/lib/deploy/secret-scan.js +65 -0
  114. package/src/lib/deploy-receipt/index.js +2 -2
  115. package/src/lib/detect-context.js +2 -2
  116. package/src/lib/execution-approvals.js +59 -0
  117. package/src/lib/knowledge-docker.js +6 -3
  118. package/src/lib/local-sessions.js +148 -0
  119. package/src/lib/local-status.js +1 -1
  120. package/src/lib/project-config.js +1 -1
  121. package/src/lib/provider-credentials.js +183 -0
  122. package/src/lib/scaffold/templates/worker-api/index.js +101 -20
  123. package/src/lib/scaffold/wizards/worker-api.js +27 -11
  124. package/src/lib/slash-commands.js +23 -16
  125. package/src/local/migrations.js +93 -0
  126. package/src/local/runtime-store.js +141 -0
  127. package/src/local/sqlite.js +2 -0
  128. package/src/local-pty/server.js +113 -51
  129. package/src/models/catalog.js +135 -0
  130. package/src/models/discovery.js +292 -0
  131. package/src/models/index.js +7 -0
  132. package/src/providers/anthropic-messages.js +192 -0
  133. package/src/providers/cloudflare-chat.js +183 -0
  134. package/src/providers/factory.js +69 -0
  135. package/src/providers/gemini-generate-content.js +208 -0
  136. package/src/providers/index.js +10 -0
  137. package/src/providers/ollama-chat.js +148 -0
  138. package/src/providers/openai-responses.js +426 -0
  139. package/src/repository/index.js +14 -2
  140. package/src/rpc/generated/common_grpc_pb.js +1 -0
  141. package/src/rpc/generated/common_pb.js +536 -0
  142. package/src/rpc/generated/errors_grpc_pb.js +1 -0
  143. package/src/rpc/generated/errors_pb.js +482 -0
  144. package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
  145. package/src/rpc/generated/knowledge_pb.js +2168 -0
  146. package/src/rpc/generated/package.json +3 -0
  147. package/src/security/process.js +35 -9
  148. package/src/security/trust-boundary.js +2 -2
  149. package/src/telemetry/contracts.js +203 -0
  150. package/src/telemetry/events.js +51 -0
  151. package/src/telemetry/index.js +8 -0
  152. package/src/tools/hydrate.js +35 -0
  153. package/src/tools/index.js +1 -0
  154. package/src/ui/boot.js +15 -17
  155. package/src/ui/cli/activity.js +76 -0
  156. package/src/ui/cli/compaction.js +15 -0
  157. package/src/ui/cli/footer.js +39 -0
  158. package/src/ui/cli/help.js +192 -0
  159. package/src/ui/cli/plan.js +20 -0
  160. package/src/ui/cli/runtime-events.js +110 -0
  161. package/src/ui/cli/waiting.js +16 -0
  162. package/src/ui/merkle/render.js +1 -1
  163. package/test/account-session.test.mjs +36 -0
  164. package/test/cli/preferences-runtime.test.mjs +11 -0
  165. package/test/cli/runtime-ui.test.mjs +74 -0
  166. package/test/cli-preferences.test.mjs +26 -5
  167. package/test/cloudflare-connector.test.mjs +96 -0
  168. package/test/cloudflare-runtime.test.mjs +75 -0
  169. package/test/context.test.mjs +61 -12
  170. package/test/deploy-health-scan.test.mjs +67 -0
  171. package/test/error-diagnostics.test.mjs +115 -0
  172. package/test/eval-context.test.mjs +37 -0
  173. package/test/execution-approvals.test.mjs +27 -0
  174. package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
  175. package/test/integration/cli-help.test.mjs +37 -0
  176. package/test/integration/knowledge-rpc.test.mjs +112 -0
  177. package/test/integration/merkle-cli.test.mjs +61 -0
  178. package/test/integration/merkle-persistence-identity.test.mjs +48 -0
  179. package/test/integration/provider-env-cli.test.mjs +49 -0
  180. package/test/integration/provider-factory.test.mjs +197 -0
  181. package/test/integration/repository-company-graph.test.mjs +90 -0
  182. package/test/integration/runtime-migrations.test.mjs +82 -0
  183. package/test/knowledge-service.test.mjs +5 -0
  184. package/test/knowledge.test.mjs +16 -0
  185. package/test/live/terminal-transport.live.test.mjs +24 -0
  186. package/test/local-sessions.test.mjs +48 -0
  187. package/test/local-studio-deploy.test.mjs +83 -0
  188. package/test/model-catalog.test.mjs +43 -0
  189. package/test/models.test.mjs +127 -16
  190. package/test/npm10-lock.test.mjs +29 -0
  191. package/test/ollama.test.mjs +21 -0
  192. package/test/openai-responses.test.mjs +95 -0
  193. package/test/portable-context.test.mjs +1 -1
  194. package/test/provider-credentials.test.mjs +96 -0
  195. package/test/rehydrate.test.mjs +25 -0
  196. package/test/release-hygiene.test.mjs +13 -5
  197. package/test/responses-runner.test.mjs +150 -0
  198. package/test/shell.test.mjs +92 -23
  199. package/test/smoke.mjs +4 -1
  200. package/test/telemetry.test.mjs +79 -0
  201. package/test/terminal/local-pty.mock.test.mjs +151 -0
  202. package/test/tools-search.test.mjs +14 -1
  203. package/test/whoami-resume.test.mjs +56 -0
  204. /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
  205. /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
  206. /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
  207. /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
  208. /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
  209. /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
  210. /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
  211. /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
@@ -0,0 +1,58 @@
1
+ import path from 'node:path';
2
+ import { listWranglerNativeCommands, runWranglerNative, summarizeCloudflareCpuProfileFile, WRANGLER_OPERATION_FAMILIES } from '../cloudflare/index.js';
3
+ import { renderDiagnosticError } from '../errors/index.js';
4
+
5
+ function parse(argv = []) {
6
+ const subcommand = argv[0] || 'status';
7
+ const takesAction = subcommand === 'run' || subcommand === 'cpu';
8
+ const out = { subcommand, action: takesAction ? (argv[1] || '') : '', cwd: process.cwd(), json: false, name: '', account: '', config: '', env: '', profile: '', path: '', page: null, file: '' };
9
+ for (let i = takesAction ? 2 : 1; i < argv.length; i += 1) {
10
+ const arg = argv[i];
11
+ if (arg === '--json') out.json = true;
12
+ else if (arg === '--cwd') out.cwd = argv[++i] || out.cwd;
13
+ else if (arg === '--name') out.name = argv[++i] || '';
14
+ else if (arg === '--account') out.account = argv[++i] || '';
15
+ else if (arg === '--config') out.config = argv[++i] || '';
16
+ else if (arg === '--env') out.env = argv[++i] || '';
17
+ else if (arg === '--profile') out.profile = argv[++i] || '';
18
+ else if (arg === '--path') out.path = argv[++i] || '';
19
+ else if (arg === '--page') out.page = Number(argv[++i] || 1);
20
+ else if (arg === '--file') out.file = argv[++i] || '';
21
+ else if (arg === '--help' || arg === '-h') out.help = true;
22
+ else if (out.subcommand === 'cpu' && out.action === 'analyze' && !out.file) out.file = arg;
23
+ else throw new Error(`unknown cloudflare option: ${arg}`);
24
+ }
25
+ return out;
26
+ }
27
+
28
+ const help = `Agent Sam · Cloudflare\n\n agentsam cloudflare status [--cwd PATH] [--json]\n agentsam cloudflare commands [--json]\n agentsam cloudflare run <whoami|deployments.list|versions.list|types.check|queues.list> [options] [--json]\n agentsam cloudflare cpu analyze <profile.cpuprofile> [--cwd PATH] [--json]\n\nSafe native runner is read-only. Deploy, rollback, secret reads, D1 mutation, R2 writes, and long-running tail/dev sessions remain explicit operator actions.\n`;
29
+
30
+ export async function runCloudflare(argv = [], options = {}) {
31
+ const args = parse(argv);
32
+ if (options.cwd && !argv.includes('--cwd')) args.cwd = path.resolve(options.cwd);
33
+ const write = options.write || ((value) => process.stdout.write(value));
34
+ if (args.help) { write(help); return null; }
35
+ try {
36
+ let result;
37
+ if (args.subcommand === 'status') {
38
+ result = await runWranglerNative('whoami', args, options);
39
+ } else if (args.subcommand === 'commands') {
40
+ result = { schema_version: 1, native: listWranglerNativeCommands(), families: WRANGLER_OPERATION_FAMILIES };
41
+ } else if (args.subcommand === 'run') {
42
+ if (!args.action) throw new Error('cloudflare native command id required');
43
+ result = await runWranglerNative(args.action, args, options);
44
+ } else if (args.subcommand === 'cpu' && args.action === 'analyze') {
45
+ result = summarizeCloudflareCpuProfileFile({ cwd: path.resolve(args.cwd), file: args.file });
46
+ } else {
47
+ write(help);
48
+ return null;
49
+ }
50
+ write(args.json ? `${JSON.stringify(result)}\n` : `${JSON.stringify(result, null, 2)}\n`);
51
+ return result;
52
+ } catch (error) {
53
+ if (args.json) write(`${JSON.stringify({ ok: false, error: error?.diagnostic || { code: error?.code || 'cloudflare_operation_failed', message: error?.message || String(error) } })}\n`);
54
+ else write(`${renderDiagnosticError(error)}\n`);
55
+ error.reported = true;
56
+ throw error;
57
+ }
58
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Operator diagnostics for AgentSam connections (Cloudflare account grant).
3
+ * Identity (who is this user?) is separate from the Cloudflare connector.
4
+ */
5
+ import {
6
+ CLOUDFLARE_CALLBACK_PATH,
7
+ CLOUDFLARE_FIXTURE_CLIENT_ID,
8
+ cloudflareConnectionSafeStatus,
9
+ resolveCloudflareOAuthClient,
10
+ } from '../../packages/connectors/cloudflare/src/index.js';
11
+ import { resolveIamIssuer } from '../../packages/identity/src/contracts/auth-config.js';
12
+
13
+ const PRODUCTION_CALLBACK = `https://agentsam.inneranimalmedia.com${CLOUDFLARE_CALLBACK_PATH}`;
14
+
15
+ function doctor(env = process.env) {
16
+ const iam = {
17
+ issuer: resolveIamIssuer(env),
18
+ clientId: Boolean(String(env.IAM_CLIENT_ID || '').trim()),
19
+ serverSecret: Boolean(String(env.IAM_CLIENT_SECRET || '').trim()),
20
+ originAlias: Boolean(String(env.IAM_ORIGIN || '').trim()),
21
+ };
22
+ const cf = cloudflareConnectionSafeStatus(env);
23
+ const client = resolveCloudflareOAuthClient(env);
24
+ return { identity: iam, cloudflare: cf, client };
25
+ }
26
+
27
+ function printSetup(env = process.env) {
28
+ const client = resolveCloudflareOAuthClient(env);
29
+ console.log('Cloudflare connector setup');
30
+ console.log('');
31
+ console.log(` callback: ${PRODUCTION_CALLBACK}`);
32
+ console.log(' authorize: https://dash.cloudflare.com/oauth2/auth');
33
+ console.log(' token: https://dash.cloudflare.com/oauth2/token');
34
+ console.log(' revoke: https://dash.cloudflare.com/oauth2/revoke');
35
+ console.log('');
36
+ console.log('This CLI does not mint a real Cloudflare OAuth client.');
37
+ console.log('Fixture client id ' + CLOUDFLARE_FIXTURE_CLIENT_ID + ' is local-only and must never be installed on production.');
38
+ if (client.fixture) {
39
+ console.log('STOP: fixture credentials are loaded. OAuth start will return 503.');
40
+ } else if (client.status === 'not_configured') {
41
+ console.log('status: not_configured — production may deploy; connector stays optional.');
42
+ } else {
43
+ console.log(`status: ${client.status}`);
44
+ }
45
+ return 0;
46
+ }
47
+
48
+ export async function runConnections(args = []) {
49
+ const argv = args.filter((a) => a !== '--json');
50
+ const jsonMode = args.includes('--json');
51
+ const env = process.env;
52
+ if (argv[0] === 'cloudflare' && argv[1] === 'setup') {
53
+ if (jsonMode) {
54
+ console.log(JSON.stringify({
55
+ callback: PRODUCTION_CALLBACK,
56
+ authorize: 'https://dash.cloudflare.com/oauth2/auth',
57
+ token: 'https://dash.cloudflare.com/oauth2/token',
58
+ revoke: 'https://dash.cloudflare.com/oauth2/revoke',
59
+ mintsRealClient: false,
60
+ client: resolveCloudflareOAuthClient(env),
61
+ }, null, 2));
62
+ return 0;
63
+ }
64
+ return printSetup(env);
65
+ }
66
+ const report = doctor(env);
67
+ if (jsonMode) {
68
+ console.log(JSON.stringify({ identity: report.identity, cloudflare: report.cloudflare }, null, 2));
69
+ return 0;
70
+ }
71
+ const iam = report.identity;
72
+ const cf = report.cloudflare;
73
+ const client = report.client;
74
+ console.log('AgentSam Identity');
75
+ console.log('');
76
+ console.log('IAM client');
77
+ console.log(` ${iam.clientId ? '✓' : '•'} client id`);
78
+ console.log(` ✓ issuer ${iam.issuer}`);
79
+ console.log(` ${iam.serverSecret ? '✓' : '•'} server secret configured`);
80
+ if (iam.originAlias) console.log('Compatibility\n IAM_ORIGIN -> deprecated alias');
81
+ console.log('');
82
+ console.log('Cloudflare connection');
83
+ console.log('');
84
+ console.log('OAuth client');
85
+ console.log(` client id: ${cf.clientId}`);
86
+ console.log(` secret: ${cf.secret}`);
87
+ console.log(` status: ${client.status}`);
88
+ if (client.fixture) {
89
+ console.log(' OAuth client fixture configured');
90
+ console.log(' real Cloudflare OAuth client still required');
91
+ }
92
+ return 0;
93
+ }
@@ -0,0 +1,129 @@
1
+ import { createContextBudget, assessContextUsage } from '../context/index.js';
2
+ import { getModelRecord } from '../models/index.js';
3
+ import { readCliPreferences } from '../lib/cli-preferences.js';
4
+
5
+ function formatInteger(value) {
6
+ return Number(value).toLocaleString('en-US');
7
+ }
8
+
9
+ function percent(value) {
10
+ return `${(Number(value) * 100).toFixed(1)}%`;
11
+ }
12
+
13
+ export function buildContextEconomicsReport(cwd, options = {}) {
14
+ const preferences = options.preferences || readCliPreferences(cwd) || {};
15
+ const model = preferences.modelSnapshot?.model_key === preferences.modelPreference
16
+ ? preferences.modelSnapshot
17
+ : getModelRecord(preferences.modelPreference);
18
+ if (!model) {
19
+ return Object.freeze({
20
+ model: preferences.modelPreference || 'auto',
21
+ resolved: false,
22
+ reason: 'Select an exact catalog model with /model before Agent Sam can calculate model-specific context economics.',
23
+ reasoning_effort: preferences.reasoningEffort || 'auto',
24
+ service_tier: preferences.serviceTier || 'default',
25
+ active_input_tokens: null,
26
+ });
27
+ }
28
+
29
+ const windowTokens = Number(model.context_window);
30
+ if (!(Number.isFinite(windowTokens) && windowTokens > 0)) {
31
+ return Object.freeze({
32
+ model: model.provider_model_id,
33
+ model_key: model.model_key,
34
+ resolved: false,
35
+ reason: 'This provider verified the model but did not expose a trustworthy context-window limit. Agent Sam will show ctx unknown rather than guess.',
36
+ reasoning_effort: preferences.reasoningEffort || 'auto',
37
+ service_tier: preferences.serviceTier || 'default',
38
+ active_input_tokens: Number.isFinite(options.activeInputTokens) ? Math.floor(options.activeInputTokens) : null,
39
+ });
40
+ }
41
+
42
+ const policy = model.context_policy || {};
43
+ const budget = createContextBudget({
44
+ windowTokens,
45
+ targetInputTokens: policy.target_input_tokens,
46
+ compactAtTokens: policy.compact_at_tokens,
47
+ interveneAtTokens: policy.intervene_at_tokens,
48
+ maxNormalInputTokens: policy.max_normal_input_tokens,
49
+ pricingThresholdTokens: policy.pricing_threshold_tokens,
50
+ safetyMarginTokens: policy.safety_margin_tokens,
51
+ });
52
+ const active = Number.isFinite(options.activeInputTokens) && options.activeInputTokens >= 0
53
+ ? Math.floor(options.activeInputTokens)
54
+ : null;
55
+ const pressure = active == null ? null : assessContextUsage(active, budget);
56
+
57
+ return Object.freeze({
58
+ model: model.provider_model_id,
59
+ model_key: model.model_key,
60
+ resolved: true,
61
+ reasoning_effort: preferences.reasoningEffort || 'auto',
62
+ service_tier: preferences.serviceTier || 'default',
63
+ active_input_tokens: active,
64
+ estimate_kind: options.estimateKind === 'provider' ? 'provider' : active == null ? null : 'local',
65
+ window_tokens: budget.windowTokens,
66
+ utilization_ratio: pressure?.utilizationRatio ?? null,
67
+ target_input_tokens: budget.targetInputTokens,
68
+ compact_at_tokens: budget.compactAtTokens,
69
+ intervene_at_tokens: budget.interveneAtTokens,
70
+ max_normal_input_tokens: budget.maxNormalInputTokens,
71
+ pricing_threshold_tokens: budget.pricingThresholdTokens,
72
+ tokens_until_pricing_threshold: pressure?.tokensUntilPricingThreshold ?? null,
73
+ pressure: pressure?.stage ?? 'unknown',
74
+ should_compact: pressure?.shouldCompact ?? false,
75
+ should_intervene: pressure?.shouldIntervene ?? false,
76
+ pricing_threshold_crossed: pressure?.pricingThresholdCrossed ?? false,
77
+ batch: model.batch,
78
+ pricing_source: model.pricing?.source || null,
79
+ pricing_as_of: model.pricing?.as_of || null,
80
+ });
81
+ }
82
+
83
+ export function renderContextEconomics(report) {
84
+ if (!report.resolved) {
85
+ return [
86
+ '',
87
+ ' Agent Sam · context',
88
+ ` model ${report.model}`,
89
+ ` reasoning ${report.reasoning_effort}`,
90
+ ` processing ${report.service_tier}`,
91
+ '',
92
+ ` ${report.reason}`,
93
+ '',
94
+ ].join('\n');
95
+ }
96
+
97
+ const active = report.active_input_tokens == null
98
+ ? 'unavailable · no provider/local usage snapshot yet'
99
+ : `${report.estimate_kind === 'provider' ? '' : '~'}${formatInteger(report.active_input_tokens)}${report.utilization_ratio == null ? '' : ` · ${percent(report.utilization_ratio)} of window`}`;
100
+ const remaining = report.tokens_until_pricing_threshold == null
101
+ ? 'unavailable until active usage is known'
102
+ : `${report.tokens_until_pricing_threshold < 0 ? '-' : ''}${formatInteger(Math.abs(report.tokens_until_pricing_threshold))}`;
103
+ return [
104
+ '',
105
+ ' Agent Sam · context',
106
+ ` model ${report.model}`,
107
+ ` reasoning ${report.reasoning_effort}`,
108
+ ` processing ${report.service_tier}`,
109
+ '',
110
+ ' Context',
111
+ ` active ${active}`,
112
+ ` window ${formatInteger(report.window_tokens)}`,
113
+ '',
114
+ ' Working-set policy',
115
+ ` target ${formatInteger(report.target_input_tokens)}`,
116
+ ` compact at ${formatInteger(report.compact_at_tokens)}`,
117
+ ` intervene at ${formatInteger(report.intervene_at_tokens)}`,
118
+ ` max normal ${formatInteger(report.max_normal_input_tokens)}`,
119
+ '',
120
+ ' Economics',
121
+ ` price threshold ${formatInteger(report.pricing_threshold_tokens)}`,
122
+ ` remaining ${remaining}`,
123
+ ` pricing as-of ${report.pricing_as_of || 'unknown'}`,
124
+ '',
125
+ ' Batch is a separate asynchronous execution lane; it is not an interactive service tier.',
126
+ ' Use `/context repo` for repository/Git bridge context.',
127
+ '',
128
+ ].join('\n');
129
+ }
@@ -1,4 +1,4 @@
1
- import { tryResolveGitContext } from '../lib/git-context.js';
1
+ import { tryResolveGitContext } from '../../packages/agentsam-repository/src/git-context.js';
2
2
  import { resolveAgentSamBaseUrl, resolveBridgeKey } from '../lib/bridge-client.js';
3
3
  import { loadProjectRules } from '../lib/project-rules.js';
4
4
 
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { initializeLocalSqlite, inspectLocalSqlite } from '../local/sqlite.js';
3
+ import { createLocalSqliteDatabase, initializeLocalSqlite, inspectLocalSqlite } from '../local/sqlite.js';
4
+ import { applyRuntimeMigrations } from '../local/migrations.js';
4
5
  import { getLocalDatabasePath, getLocalSchemaPath, readProjectConfig } from '../lib/project-config.js';
5
6
 
6
7
  function findProjectRoot(startDir) {
@@ -23,7 +24,7 @@ function resolveDb(root, config) {
23
24
 
24
25
  export async function runDb(argv = [], opts = {}) {
25
26
  const sub = argv[0] || 'status';
26
- if (!['init', 'status'].includes(sub)) {
27
+ if (!['init', 'migrate', 'status'].includes(sub)) {
27
28
  throw new Error(`unknown db command: ${sub}`);
28
29
  }
29
30
 
@@ -36,10 +37,26 @@ export async function runDb(argv = [], opts = {}) {
36
37
  console.log(`\n Agent Sam local DB\n`);
37
38
  console.log(` ✓ SQLite ${result.dbPath}`);
38
39
  console.log(` ✓ Tables ${result.tables.length}`);
39
- console.log(` ✓ Schema ${paths.schemaPath}\n`);
40
+ console.log(` ✓ Schema ${paths.schemaPath}`);
41
+ console.log(' ✓ Migrations current\n');
40
42
  return result;
41
43
  }
42
44
 
45
+ if (sub === 'migrate') {
46
+ if (!fs.existsSync(paths.dbPath)) throw new Error('Local DB is not initialized — run `agentsam db init` first.');
47
+ const db = await createLocalSqliteDatabase(paths.dbPath);
48
+ try {
49
+ const result = await applyRuntimeMigrations(db);
50
+ console.log(`\n Agent Sam local DB migrations\n`);
51
+ console.log(` applied ${result.applied}`);
52
+ console.log(` known ${result.total}`);
53
+ console.log(` path ${paths.dbPath}\n`);
54
+ return result;
55
+ } finally {
56
+ db.close();
57
+ }
58
+ }
59
+
43
60
  const result = await inspectLocalSqlite(paths.dbPath);
44
61
  console.log(`\n Agent Sam local DB\n`);
45
62
  if (!result.exists) {
@@ -6,8 +6,9 @@ import path from 'node:path';
6
6
  import readline from 'node:readline';
7
7
  import { authenticateViaBrowser } from '../lib/auth.js';
8
8
  import { getJson, streamScaffold } from '../lib/core-client.js';
9
- import { resolveSdkKey } from '../../packages/identity/src/contracts/auth-config.js';
9
+ import { resolveAccountSdkKey } from '../lib/account-session.js';
10
10
  import { getDefaultProfile, getDeployTarget, getLocalSchemaPath, getProjectName, getProjectPreset, readProjectConfig, setDeployTarget, writeProjectConfig } from '../lib/project-config.js';
11
+ import { isLocalStudioCheckout, runLocalStudioDeploy } from '../lib/deploy/local-studio.js';
11
12
 
12
13
  function writeCloudflareAdapter(cwd, config, cf) {
13
14
  const projectName = getProjectName(config, path.basename(cwd));
@@ -67,7 +68,7 @@ function ask(question) {
67
68
  async function runCloudflareDeploy(cwd, config, accountId) {
68
69
  console.log('\n Cloudflare deploy — browser sign-in + resource provisioning…\n');
69
70
 
70
- let token = resolveSdkKey(process.env);
71
+ let token = resolveAccountSdkKey({ env: process.env }).value;
71
72
  if (!token) {
72
73
  const session = await authenticateViaBrowser();
73
74
  token = session.access_token;
@@ -122,10 +123,45 @@ async function runCloudflareDeploy(cwd, config, accountId) {
122
123
  }
123
124
 
124
125
  /**
125
- * @param {{ cwd?: string, target?: string, accountId?: string }} [opts]
126
+ * @param {{ cwd?: string, target?: string, accountId?: string, dryRun?: boolean, plan?: boolean }} [opts]
126
127
  */
127
128
  export async function runDeploy(opts = {}) {
128
129
  const cwd = path.resolve(opts.cwd || process.cwd());
130
+
131
+ if (isLocalStudioCheckout(cwd)) {
132
+ const result = await runLocalStudioDeploy({
133
+ cwd,
134
+ dryRun: Boolean(opts.dryRun),
135
+ planOnly: Boolean(opts.plan),
136
+ execute: !opts.plan,
137
+ });
138
+ const payload = {
139
+ provider: result.plan.provider,
140
+ app: result.plan.app,
141
+ wranglerConfig: result.plan.wranglerConfig,
142
+ wranglerArgs: result.plan.wranglerArgs,
143
+ cwd: result.plan.cwd,
144
+ envLoaded: result.plan.envLoaded,
145
+ fingerprint: result.plan.fingerprint,
146
+ skip: result.plan.skip,
147
+ dryRun: Boolean(opts.dryRun),
148
+ plan: Boolean(opts.plan),
149
+ genericRootDeploy: false,
150
+ receipt: result.receipt,
151
+ };
152
+ console.log(JSON.stringify(payload, null, 2));
153
+ if (result.plan.skip) {
154
+ console.log('skip: unchanged deploy fingerprint');
155
+ } else if (opts.plan) {
156
+ console.log('plan only — no wrangler deploy');
157
+ } else if (opts.dryRun) {
158
+ console.log('wrangler dry-run complete');
159
+ } else {
160
+ console.log('local-studio deploy complete');
161
+ }
162
+ return result;
163
+ }
164
+
129
165
  const config = readProjectConfig(cwd);
130
166
 
131
167
  let target = opts.target || getDeployTarget(config) || 'cloudflare';
@@ -0,0 +1,90 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { describeProviderCredential, ensureProviderEnvProfile, providerCredentialSpec } from '../lib/provider-credentials.js';
3
+
4
+ const PROVIDERS = Object.freeze(['openai', 'anthropic', 'gemini', 'grok', 'cloudflare']);
5
+
6
+ function writeLine(write, value = '') { write(`${value}\n`); }
7
+
8
+ function detectCloudflareAccounts(options = {}) {
9
+ const env = options.env || process.env;
10
+ const explicit = String(options.accountId || env.ACCOUNT_ID || env.CLOUDFLARE_ACCOUNT_ID || '').trim();
11
+ if (explicit) return { accounts: [{ id: explicit, name: null }], source: 'environment' };
12
+ const spawn = options.spawnSyncImpl || spawnSync;
13
+ const result = spawn('npx', ['--no-install', 'wrangler', 'whoami', '--json'], { cwd: options.cwd || process.cwd(), env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
14
+ if (result?.status !== 0) return { accounts: [], source: 'wrangler', error: String(result?.stderr || '').trim() || `wrangler exited ${result?.status ?? 'unknown'}` };
15
+ try {
16
+ const parsed = JSON.parse(String(result.stdout || '{}'));
17
+ const accounts = (Array.isArray(parsed?.accounts) ? parsed.accounts : []).map((row) => ({
18
+ id: String(row?.id || row?.account_id || '').trim(),
19
+ name: String(row?.name || row?.account_name || '').trim() || null,
20
+ })).filter((row) => row.id);
21
+ return { accounts, source: 'wrangler', error: null };
22
+ } catch (error) {
23
+ return { accounts: [], source: 'wrangler', error: `invalid wrangler JSON: ${error?.message || error}` };
24
+ }
25
+ }
26
+
27
+ function cloudflareGuidance(write) {
28
+ writeLine(write, '');
29
+ writeLine(write, ' Cloudflare token guidance');
30
+ writeLine(write, ' models only Workers AI Read');
31
+ writeLine(write, ' run Workers AI Workers AI Read + Edit');
32
+ writeLine(write, ' deploy Workers Workers Editor for existing Workers; Admin only when create/delete is required');
33
+ writeLine(write, ' routes/domains add Workers Routes Write only when AgentSam must change them');
34
+ writeLine(write, ' D1 / R2 / KV add direct product permissions only when AgentSam must read/write those resources directly');
35
+ }
36
+
37
+ export async function runEnv(argv = [], options = {}) {
38
+ const write = options.write || ((text) => process.stdout.write(text));
39
+ const [command = 'status', providerArg] = argv;
40
+ let accountId = '';
41
+ for (let i = 2; i < argv.length; i += 1) {
42
+ if (argv[i] === '--account-id') accountId = String(argv[++i] || '').trim();
43
+ else throw new Error(`unexpected env argument: ${argv[i]}`);
44
+ }
45
+ if (command === '--help' || command === '-h' || command === 'help') {
46
+ writeLine(write, 'agentsam env init <openai|anthropic|gemini|grok|cloudflare> [--account-id <id>]');
47
+ writeLine(write, 'agentsam env status [provider]');
48
+ return;
49
+ }
50
+ if (command === 'init') {
51
+ const provider = String(providerArg || '').trim().toLowerCase();
52
+ if (!PROVIDERS.includes(provider)) throw new Error(`env init requires one of: ${PROVIDERS.join(', ')}`);
53
+ let cloudflareAccounts = null;
54
+ if (provider === 'cloudflare') {
55
+ if (accountId && !/^[a-f0-9]{32}$/i.test(accountId)) throw new Error('Cloudflare --account-id must be a 32-character hexadecimal account ID');
56
+ cloudflareAccounts = detectCloudflareAccounts({ ...options, accountId });
57
+ if (!accountId && cloudflareAccounts.accounts.length === 1) accountId = cloudflareAccounts.accounts[0].id;
58
+ }
59
+ const result = ensureProviderEnvProfile(provider, { ...options, accountId });
60
+ writeLine(write, '');
61
+ writeLine(write, ` AgentSam · ${provider} environment`);
62
+ writeLine(write, ` profile ${result.file}${result.created ? ' · created' : ' · existing'}`);
63
+ writeLine(write, ` loader ${result.loader}`);
64
+ writeLine(write, '');
65
+ writeLine(write, ' Add the credential to the profile, then load it into this shell:');
66
+ writeLine(write, ` ${result.source_command}`);
67
+ if (provider === 'cloudflare') {
68
+ if (accountId) writeLine(write, ` account detected/configured (${cloudflareAccounts?.source || 'explicit'})`);
69
+ else if ((cloudflareAccounts?.accounts || []).length > 1) writeLine(write, ` account ${cloudflareAccounts.accounts.length} Wrangler accounts found · rerun with --account-id <id>`);
70
+ else writeLine(write, ' account not detected · set ACCOUNT_ID in the profile or rerun with --account-id <id>');
71
+ cloudflareGuidance(write);
72
+ }
73
+ writeLine(write, '');
74
+ return result;
75
+ }
76
+ if (command === 'status') {
77
+ const providers = providerArg ? [String(providerArg).trim().toLowerCase()] : PROVIDERS;
78
+ for (const provider of providers) if (!providerCredentialSpec(provider)) throw new Error(`unsupported_provider:${provider}`);
79
+ writeLine(write, '');
80
+ writeLine(write, ' AgentSam · provider environments');
81
+ for (const provider of providers) {
82
+ const row = describeProviderCredential(provider, options);
83
+ const extra = row.account_id ? ` · account ${row.account_id}` : '';
84
+ writeLine(write, ` ${provider.padEnd(12)} ${row.configured ? 'configured' : row.error ? `blocked (${row.error})` : 'not configured'}${extra}`);
85
+ }
86
+ writeLine(write, '');
87
+ return;
88
+ }
89
+ throw new Error(`unknown env command: ${command}`);
90
+ }
@@ -0,0 +1,63 @@
1
+ import { evaluateContextFixture, listContextEvalFixtures } from '../eval/index.js';
2
+
3
+ function parse(argv) {
4
+ const out = { subcommand: argv[0] || '', fixture: '', strategy: 'all', model: 'gpt-6-astra', json: false, list: false };
5
+ for (let i = 1; i < argv.length; i += 1) {
6
+ const arg = argv[i];
7
+ if (arg === '--fixture') out.fixture = argv[++i] || '';
8
+ else if (arg === '--strategy') out.strategy = argv[++i] || 'all';
9
+ else if (arg === '--model') out.model = argv[++i] || 'gpt-6-astra';
10
+ else if (arg === '--json') out.json = true;
11
+ else if (arg === '--list') out.list = true;
12
+ else if (arg === '--help' || arg === '-h') out.help = true;
13
+ else throw new Error(`unknown eval option: ${arg}`);
14
+ }
15
+ return out;
16
+ }
17
+
18
+ function render(report) {
19
+ const rows = [
20
+ '',
21
+ ' AgentSam · Context Eval',
22
+ '',
23
+ ` fixture ${report.fixture}`,
24
+ ` model ${report.model}`,
25
+ ` provider call no · deterministic/offline`,
26
+ '',
27
+ ];
28
+ for (const row of report.strategies) {
29
+ rows.push(` ${row.strategy.toUpperCase()}`);
30
+ rows.push(` result ${row.result}`);
31
+ rows.push(` evidence ${row.required_found} / ${row.required_evidence} required · ${row.sources_selected} / ${row.sources_considered} selected`);
32
+ rows.push(` active context ~${row.active_context_tokens.toLocaleString('en-US')}`);
33
+ rows.push(` window ${row.window_tokens.toLocaleString('en-US')}`);
34
+ rows.push(` price threshold ${row.pricing_threshold_tokens.toLocaleString('en-US')}`);
35
+ rows.push(` remaining ${row.tokens_until_pricing_threshold.toLocaleString('en-US')}`);
36
+ rows.push(` tool schemas ${row.hydrated_tools} · ${row.tool_schema_chars.toLocaleString('en-US')} chars`);
37
+ rows.push(` compacted ${row.compacted_chars.toLocaleString('en-US')} chars`);
38
+ rows.push(` rehydrated ${row.rehydrated_refs.length ? row.rehydrated_refs.join(', ') : 'none'}`);
39
+ rows.push(` est input cost $${row.estimated_input_cost_usd.toFixed(4)}`);
40
+ rows.push('');
41
+ }
42
+ rows.push(` winner ${report.winner}`);
43
+ rows.push(` scoring ${report.scoring.join(' → ')}`);
44
+ rows.push('');
45
+ return rows.join('\n');
46
+ }
47
+
48
+ export async function runEval(argv = [], options = {}) {
49
+ const args = parse(argv);
50
+ const write = options.write || (text => process.stdout.write(text));
51
+ if (args.help || args.subcommand !== 'context') {
52
+ write('agentsam eval context --fixture <name> [--strategy bounded|discovery|compact|all] [--model gpt-6-astra] [--json]\n');
53
+ return null;
54
+ }
55
+ if (args.list) {
56
+ const value = { fixtures: listContextEvalFixtures() };
57
+ write(args.json ? `${JSON.stringify(value)}\n` : `${value.fixtures.join('\n')}\n`);
58
+ return value;
59
+ }
60
+ const report = await evaluateContextFixture({ fixture: args.fixture || 'exact-symbol-callers', strategy: args.strategy, model: args.model });
61
+ write(args.json ? `${JSON.stringify(report)}\n` : render(report));
62
+ return report;
63
+ }
@@ -7,15 +7,12 @@ export async function runInteractive(options = {}) {
7
7
  let identity = detectCliProject(options.cwd || process.cwd());
8
8
  let preferences = readCliPreferences(identity.root);
9
9
 
10
- if (!preferences) {
10
+ if (!preferences || preferences.trustedDirectory !== true) {
11
11
  const configured = await configureCliPreferences({ cwd: identity.root, firstRun: true });
12
12
  identity = configured.identity;
13
13
  preferences = configured.preferences;
14
14
  }
15
15
 
16
16
  await runBootScene({ identity, preferences, animate: options.animate !== false });
17
- await runShell([], {
18
- cwd: identity.root,
19
- intro: 'quiet',
20
- });
17
+ await runShell([], { cwd: identity.root, intro: 'quiet' });
21
18
  }
@@ -7,13 +7,13 @@ import { promisify } from 'node:util';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import { randomUUID } from 'node:crypto';
9
9
  import pkg from '../../package.json' with { type: 'json' };
10
- import { CONFIG_PATH, repositoryRoot, initRepository, readConfig, scopeKey, cacheNamespace } from '../knowledge/config.js';
10
+ import { CONFIG_PATH, repositoryRoot, initRepository, readConfig, defaultConfig, scopeKey, cacheNamespace } from '../knowledge/config.js';
11
11
  import { openSqliteStore } from '../knowledge/stores/sqlite.js';
12
12
  import { openPostgresStore } from '../knowledge/stores/postgres.js';
13
13
  import { planIndex, runIndex, retrieve } from '../knowledge/engine.js';
14
14
  import { createGeminiEmbedder } from '../knowledge/providers/gemini.js';
15
15
  import { compareObservations } from '../knowledge/evolution.js';
16
- import { ensureProjectManifest, getProjectName, getRepositoryId } from '../lib/project-config.js';
16
+ import { ensureProjectManifest, getProjectName, getRepositoryId, portableRepositoryIdFromGit, tryReadProjectConfig } from '../lib/project-config.js';
17
17
  import { ensureProjectRules } from '../lib/project-rules.js';
18
18
 
19
19
  const execute = promisify(execFile);
@@ -25,6 +25,14 @@ async function openStore(root, config, readOnly = false) {
25
25
  return config.storage.driver === 'sqlite' ? openSqliteStore(localPath(root), { readOnly }) : openPostgresStore(process.env[config.storage.connection_env]);
26
26
  }
27
27
  function provider() { return createGeminiEmbedder({ apiKey: process.env.GEMINI_API_KEY }); }
28
+ function resolveKnowledgeConfig(root) {
29
+ const filename = path.join(root, CONFIG_PATH);
30
+ if (fs.existsSync(filename)) return readConfig(root);
31
+ const project = tryReadProjectConfig(root);
32
+ const repositoryId = getRepositoryId(project) || portableRepositoryIdFromGit(root);
33
+ if (!repositoryId) throw new Error(`${CONFIG_PATH} is absent and repository identity could not be derived; run agentsam init . --yes to configure this repository.`);
34
+ return defaultConfig({ repositoryId });
35
+ }
28
36
 
29
37
  export async function runRepositoryInit(argv) {
30
38
  const { values: opts, positionals } = flags(argv, { existing: { type: 'boolean' }, yes: { type: 'boolean', short: 'y' }, include: { type: 'string' }, exclude: { type: 'string' }, scope: { type: 'string' }, target: { type: 'string' }, dimensions: { type: 'string' } });
@@ -73,7 +81,7 @@ export async function runKnowledge(argv) {
73
81
  const command = positionals[0] || 'plan';
74
82
  if (opts.help) { console.log('agentsam index plan|run|status|history|show|setup-store [--cwd PATH] [--embed] [--max-inputs 100] [--max-characters 200000] [--generation ID] [--json]\nplan is read-only; run defaults to AST/text only; --embed sends selected chunks to the configured provider.'); return; }
75
83
  if (positionals.length > 1 || !['plan', 'run', 'status', 'history', 'show', 'setup-store'].includes(command)) throw new Error('Unknown index command; use agentsam index --help.');
76
- const root = repositoryRoot(opts.cwd), config = readConfig(root);
84
+ const root = repositoryRoot(opts.cwd), config = resolveKnowledgeConfig(root);
77
85
  const store = await openStore(root, config, !['run', 'setup-store'].includes(command));
78
86
  try {
79
87
  if (command === 'setup-store') {
@@ -97,7 +105,7 @@ export async function runKnowledge(argv) {
97
105
  export async function runSearch(argv) {
98
106
  const { values: opts, positionals } = flags(argv, { semantic: { type: 'boolean' }, 'top-k': { type: 'string' }, 'token-budget': { type: 'string' }, generation: { type: 'string' } });
99
107
  if (opts.help) { console.log('agentsam search "query" [--cwd PATH] [--semantic] [--top-k 8] [--token-budget 6000] [--generation ID]'); return; }
100
- const root = repositoryRoot(opts.cwd), config = readConfig(root), store = await openStore(root, config, true);
108
+ const root = repositoryRoot(opts.cwd), config = resolveKnowledgeConfig(root), store = await openStore(root, config, true);
101
109
  try { show(await retrieve({ store, config, text: positionals.join(' '), semantic: opts.semantic, embedder: opts.semantic ? provider() : null, topK: Number(opts['top-k'] || 8), tokenBudget: Number(opts['token-budget'] || 6000), generationId: opts.generation })); }
102
110
  finally { await store?.close(); }
103
111
  }