@inneranimalmedia/agentsam-sdk 2.5.0 → 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 (112) 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/RELEASES.md +16 -7
  7. package/package.json +20 -8
  8. package/packages/connectors/cloudflare/package.json +10 -0
  9. package/packages/connectors/cloudflare/src/index.js +127 -0
  10. package/packages/connectors/cloudflare/src/owner.js +76 -0
  11. package/packages/connectors/cloudflare/src/routes.js +223 -0
  12. package/packages/connectors/cloudflare/src/vault.js +80 -0
  13. package/packages/connectors/cloudflare/tests/connector.test.mjs +44 -0
  14. package/packages/identity/package.json +2 -2
  15. package/packages/identity/src/contracts/auth-config.js +18 -7
  16. package/packages/identity/tests/auth-config.test.mjs +9 -5
  17. package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
  18. package/protocol/README.md +1 -0
  19. package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +19 -0
  20. package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +13 -0
  21. package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +19 -0
  22. package/protocol/capabilities/manifest.json +47 -0
  23. package/protocol/context/context-budget.schema.json +10 -15
  24. package/protocol/context/context-item.schema.json +4 -5
  25. package/protocol/context/resolved-context-pack.schema.json +19 -14
  26. package/protocol/models/README.md +373 -0
  27. package/protocol/models/model-inventory-v2.schema.json +212 -0
  28. package/skills/agentsam-cloudflare-workers/SKILL.md +53 -0
  29. package/skills/agentsam-cloudflare-workers/references/cpu-profiling.md +16 -0
  30. package/skills/agentsam-cloudflare-workers/references/errors-and-observability.md +29 -0
  31. package/skills/agentsam-cloudflare-workers/references/wrangler-native-map.md +28 -0
  32. package/skills/catalog.json +18 -0
  33. package/src/agent/capability-adapter.js +25 -13
  34. package/src/agent/index.js +1 -0
  35. package/src/agent/responses-runner.js +325 -0
  36. package/src/cli.js +98 -28
  37. package/src/cloudflare/cpu-profile.js +115 -0
  38. package/src/cloudflare/index.js +14 -0
  39. package/src/cloudflare/wrangler.js +132 -0
  40. package/src/commands/account-auth.js +47 -0
  41. package/src/commands/cloudflare.js +58 -0
  42. package/src/commands/connections.js +93 -0
  43. package/src/commands/context-economics.js +114 -0
  44. package/src/commands/deploy.js +39 -3
  45. package/src/commands/eval.js +63 -0
  46. package/src/commands/interactive.js +2 -5
  47. package/src/commands/models.js +85 -40
  48. package/src/commands/preferences.js +101 -59
  49. package/src/commands/resume.js +67 -0
  50. package/src/commands/security.js +5 -3
  51. package/src/commands/shell.js +370 -109
  52. package/src/commands/tunnel.js +2 -2
  53. package/src/commands/whoami.js +86 -0
  54. package/src/context/budget.js +68 -6
  55. package/src/context/index.js +3 -1
  56. package/src/context/rehydrate.js +35 -0
  57. package/src/context/resolve.js +44 -12
  58. package/src/errors/diagnostic.js +160 -0
  59. package/src/errors/index.js +9 -0
  60. package/src/eval/context.js +191 -0
  61. package/src/eval/index.js +1 -0
  62. package/src/index.js +55 -1
  63. package/src/lib/account-session.js +98 -0
  64. package/src/lib/agent-instructions.js +73 -0
  65. package/src/lib/auth.js +4 -0
  66. package/src/lib/cli-preferences.js +28 -24
  67. package/src/lib/deploy/git-guard.js +69 -0
  68. package/src/lib/deploy/health.js +57 -0
  69. package/src/lib/deploy/local-studio.js +283 -0
  70. package/src/lib/deploy/secret-scan.js +65 -0
  71. package/src/lib/detect-context.js +2 -2
  72. package/src/lib/execution-approvals.js +59 -0
  73. package/src/lib/local-sessions.js +127 -0
  74. package/src/lib/provider-credentials.js +83 -0
  75. package/src/lib/scaffold/templates/worker-api/index.js +101 -20
  76. package/src/lib/scaffold/wizards/worker-api.js +27 -11
  77. package/src/lib/slash-commands.js +22 -16
  78. package/src/models/catalog.js +135 -0
  79. package/src/models/index.js +7 -0
  80. package/src/providers/index.js +5 -0
  81. package/src/providers/openai-responses.js +275 -0
  82. package/src/security/process.js +35 -9
  83. package/src/telemetry/contracts.js +203 -0
  84. package/src/telemetry/events.js +48 -0
  85. package/src/telemetry/index.js +8 -0
  86. package/src/tools/hydrate.js +35 -0
  87. package/src/tools/index.js +1 -0
  88. package/src/ui/boot.js +15 -17
  89. package/test/account-session.test.mjs +36 -0
  90. package/test/cli-preferences.test.mjs +26 -5
  91. package/test/cloudflare-connector.test.mjs +96 -0
  92. package/test/cloudflare-runtime.test.mjs +75 -0
  93. package/test/context.test.mjs +61 -12
  94. package/test/deploy-health-scan.test.mjs +67 -0
  95. package/test/error-diagnostics.test.mjs +59 -0
  96. package/test/eval-context.test.mjs +37 -0
  97. package/test/execution-approvals.test.mjs +27 -0
  98. package/test/local-sessions.test.mjs +42 -0
  99. package/test/local-studio-deploy.test.mjs +83 -0
  100. package/test/model-catalog.test.mjs +43 -0
  101. package/test/models.test.mjs +30 -16
  102. package/test/npm10-lock.test.mjs +29 -0
  103. package/test/openai-responses.test.mjs +95 -0
  104. package/test/provider-credentials.test.mjs +52 -0
  105. package/test/rehydrate.test.mjs +25 -0
  106. package/test/release-hygiene.test.mjs +4 -4
  107. package/test/responses-runner.test.mjs +148 -0
  108. package/test/shell.test.mjs +47 -20
  109. package/test/smoke.mjs +4 -1
  110. package/test/telemetry.test.mjs +79 -0
  111. package/test/tools-search.test.mjs +14 -1
  112. package/test/whoami-resume.test.mjs +56 -0
@@ -1,8 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { spawnSync } from 'node:child_process';
4
- import { cancel, intro, isCancel, outro, select, text } from '@clack/prompts';
4
+ import { cancel, confirm, intro, isCancel, outro, select, text } from '@clack/prompts';
5
5
  import { collectModelsStatus } from './models.js';
6
+ import { getModelRecord } from '../models/index.js';
6
7
  import { detectCliProject, readCliPreferences, writeCliPreferences } from '../lib/cli-preferences.js';
7
8
 
8
9
  function stopIfCancelled(value) {
@@ -22,81 +23,122 @@ export function availableShells(env = process.env) {
22
23
  const values = [];
23
24
  const detected = path.basename(env.SHELL || env.ComSpec || '').trim();
24
25
  if (detected) values.push(detected);
25
- for (const shell of ['zsh', 'bash', 'fish', 'pwsh']) {
26
- if (!values.includes(shell) && commandExists(shell)) values.push(shell);
27
- }
26
+ for (const shell of ['zsh', 'bash', 'fish', 'pwsh']) if (!values.includes(shell) && commandExists(shell)) values.push(shell);
28
27
  return values.length ? values : ['shell'];
29
28
  }
30
29
 
31
- function modelOptions(status) {
32
- const options = [{ value: 'auto', label: 'Automatic', hint: 'connected host/runtime decides' }];
30
+ export function modelOptions(status) {
31
+ const options = [{ value: 'auto', label: 'Automatic', hint: 'runtime chooses; exact economics unavailable until resolved' }];
32
+ for (const model of status.availableModels || []) {
33
+ options.push({ value: model.model_key, label: model.label, hint: `${model.provider_model_id} · provider verified` });
34
+ }
33
35
  if (status.local?.online) {
34
- for (const row of status.local.models || []) {
35
- if (row?.name) options.push({ value: `ollama:${row.name}`, label: `Ollama · ${row.name}`, hint: 'local' });
36
- }
36
+ for (const row of status.local.models || []) if (row?.name) options.push({ value: `ollama:${row.name}`, label: `Ollama · ${row.name}`, hint: 'local' });
37
37
  }
38
38
  for (const provider of status.providers || []) {
39
- if (provider.configured) options.push({ value: `${provider.id}:auto`, label: provider.label, hint: 'provider configured' });
39
+ if (provider.configured && !options.some((row) => row.value.startsWith(`${provider.id}:`) && row.value !== `${provider.id}:auto`)) {
40
+ options.push({ value: `${provider.id}:auto`, label: `${provider.label} · automatic`, hint: 'credential proven; exact model not verified' });
41
+ }
40
42
  }
41
43
  return options;
42
44
  }
43
45
 
44
- export async function configureCliPreferences({ cwd = process.cwd(), firstRun = false } = {}) {
46
+ function reasoningOptions(modelPreference) {
47
+ const record = getModelRecord(modelPreference);
48
+ if (!record) return [{ value: 'auto', label: 'Automatic', hint: 'runtime/provider default' }];
49
+ return record.reasoning_efforts.map((value) => ({
50
+ value,
51
+ label: value === 'xhigh' ? 'Extra high' : value === 'max' ? 'Max' : value[0].toUpperCase() + value.slice(1),
52
+ hint: value === 'low' ? 'lighter reasoning' : value === 'max' ? 'highest supported reasoning depth' : undefined,
53
+ }));
54
+ }
55
+
56
+ function serviceTierOptions(modelPreference) {
57
+ const record = getModelRecord(modelPreference);
58
+ if (!record) return [{ value: 'default', label: 'Standard', hint: 'default provider processing' }];
59
+ return record.service_tiers.map((value) => {
60
+ if (value === 'fast') return { value, label: 'Fast', hint: 'lower latency · 2× applicable token rates for Astra' };
61
+ if (value === 'flex') return { value, label: 'Flex', hint: 'slower / capacity-sensitive · 50% of Standard for Astra' };
62
+ return { value, label: 'Standard', hint: 'standard pricing and latency' };
63
+ });
64
+ }
65
+
66
+ async function promptModelPreferences(identity, existing, options = {}) {
67
+ let status = { providers: [], availableModels: [], local: { online: false, models: [] } };
68
+ try { status = await collectModelsStatus(options.modelStatusOptions || {}); } catch { /* inventory remains best-effort */ }
69
+ const models = modelOptions(status);
70
+ const initialModel = models.some((row) => row.value === existing.modelPreference) ? existing.modelPreference : 'auto';
71
+ const modelPreference = stopIfCancelled(await select({ message: 'Model', initialValue: initialModel, options: models }));
72
+
73
+ const reasoning = reasoningOptions(modelPreference);
74
+ const initialReasoning = reasoning.some((row) => row.value === existing.reasoningEffort) ? existing.reasoningEffort : reasoning[0].value;
75
+ const reasoningEffort = stopIfCancelled(await select({ message: 'Reasoning level', initialValue: initialReasoning, options: reasoning }));
76
+
77
+ const tiers = serviceTierOptions(modelPreference);
78
+ const initialTier = tiers.some((row) => row.value === existing.serviceTier) ? existing.serviceTier : tiers[0].value;
79
+ const serviceTier = stopIfCancelled(await select({ message: 'Processing', initialValue: initialTier, options: tiers }));
80
+
81
+ return { modelPreference, reasoningEffort, serviceTier };
82
+ }
83
+
84
+ export async function configureCliPreferences({ cwd = process.cwd(), firstRun = false, section = 'all', modelStatusOptions } = {}) {
45
85
  let identity = detectCliProject(cwd);
46
86
  const existing = readCliPreferences(identity.root) || {};
47
- intro(firstRun ? 'Welcome to Agent Sam' : 'Agent Sam settings');
87
+ intro(firstRun ? `You are in ${identity.root}` : section === 'model' ? 'Agent Sam model' : 'Agent Sam settings');
48
88
 
49
- const projectChoice = stopIfCancelled(await select({
50
- message: 'Where should Agent Sam work?',
51
- initialValue: 'current',
52
- options: [
53
- { value: 'current', label: `This project · ${identity.project}`, hint: identity.root },
54
- { value: 'other', label: 'Choose another folder' },
55
- ],
56
- }));
57
-
58
- if (projectChoice === 'other') {
59
- const folder = stopIfCancelled(await text({
60
- message: 'Project folder',
61
- placeholder: identity.root,
62
- defaultValue: identity.root,
63
- validate(value) {
64
- const resolved = path.resolve(String(value || ''));
65
- if (!fs.existsSync(resolved)) return 'Folder does not exist';
66
- if (!fs.statSync(resolved).isDirectory()) return 'Path is not a directory';
67
- },
89
+ let trustedDirectory = existing.trustedDirectory === true;
90
+ if (firstRun && !trustedDirectory) {
91
+ trustedDirectory = stopIfCancelled(await confirm({
92
+ message: 'Do you trust the contents of this directory? Project-local instructions, hooks, and execution policy may load.',
93
+ initialValue: false,
68
94
  }));
69
- identity = detectCliProject(path.resolve(String(folder)));
95
+ if (!trustedDirectory) {
96
+ cancel('Directory not trusted. Agent Sam did not load project-local configuration.');
97
+ const error = new Error('directory_not_trusted');
98
+ error.code = 'AGENTSAM_SETUP_CANCELLED';
99
+ throw error;
100
+ }
70
101
  }
71
102
 
72
- const runtime = stopIfCancelled(await select({
73
- message: 'Runtime',
74
- initialValue: existing.runtime || 'local',
75
- options: [
76
- { value: 'local', label: 'Local machine', hint: 'real shell + filesystem' },
77
- { value: 'remote', label: 'Remote', hint: 'connected remote runtime' },
78
- { value: 'sandbox', label: 'Sandbox', hint: 'isolated disposable runtime' },
79
- ],
80
- }));
81
-
82
- const shells = availableShells();
83
- const terminal = stopIfCancelled(await select({
84
- message: 'Terminal',
85
- initialValue: existing.terminal && shells.includes(existing.terminal) ? existing.terminal : shells[0],
86
- options: shells.map((shell) => ({ value: shell, label: shell, hint: shell === shells[0] ? 'detected/default' : undefined })),
87
- }));
88
-
89
- let status = { providers: [], local: { online: false, models: [] } };
90
- try { status = await collectModelsStatus(); } catch { /* inventory is best-effort */ }
91
- const models = modelOptions(status);
92
- const initialModel = models.some((row) => row.value === existing.modelPreference) ? existing.modelPreference : 'auto';
93
- const modelPreference = stopIfCancelled(await select({
94
- message: 'Model preference',
95
- initialValue: initialModel,
96
- options: models,
97
- }));
103
+ let runtime = existing.runtime || 'local';
104
+ let terminal = existing.terminal || availableShells()[0];
105
+ if (!firstRun && section === 'all') {
106
+ const projectChoice = stopIfCancelled(await select({
107
+ message: 'Where should Agent Sam work?',
108
+ initialValue: 'current',
109
+ options: [
110
+ { value: 'current', label: `This project · ${identity.project}`, hint: identity.root },
111
+ { value: 'other', label: 'Choose another folder' },
112
+ ],
113
+ }));
114
+ if (projectChoice === 'other') {
115
+ const folder = stopIfCancelled(await text({
116
+ message: 'Project folder', placeholder: identity.root, defaultValue: identity.root,
117
+ validate(value) {
118
+ const resolved = path.resolve(String(value || ''));
119
+ if (!fs.existsSync(resolved)) return 'Folder does not exist';
120
+ if (!fs.statSync(resolved).isDirectory()) return 'Path is not a directory';
121
+ },
122
+ }));
123
+ identity = detectCliProject(path.resolve(String(folder)));
124
+ }
125
+ runtime = stopIfCancelled(await select({
126
+ message: 'Runtime', initialValue: runtime,
127
+ options: [
128
+ { value: 'local', label: 'Local machine', hint: 'real shell + filesystem' },
129
+ { value: 'remote', label: 'Remote', hint: 'connected remote runtime' },
130
+ { value: 'sandbox', label: 'Sandbox', hint: 'isolated disposable runtime' },
131
+ ],
132
+ }));
133
+ const shells = availableShells();
134
+ terminal = stopIfCancelled(await select({
135
+ message: 'Terminal', initialValue: terminal && shells.includes(terminal) ? terminal : shells[0],
136
+ options: shells.map((shell) => ({ value: shell, label: shell, hint: shell === shells[0] ? 'detected/default' : undefined })),
137
+ }));
138
+ }
98
139
 
99
- const preferences = writeCliPreferences(identity.root, { runtime, terminal, modelPreference });
140
+ const model = await promptModelPreferences(identity, existing, { modelStatusOptions });
141
+ const preferences = writeCliPreferences(identity.root, { trustedDirectory, runtime, terminal, ...model });
100
142
  outro(`Ready · ${identity.project}`);
101
143
  return { identity, preferences };
102
144
  }
@@ -0,0 +1,67 @@
1
+ import path from 'node:path';
2
+ import { isCancel, select } from '@clack/prompts';
3
+ import { listLocalSessions, loadLocalSession } from '../lib/local-sessions.js';
4
+ import { runShell } from './shell.js';
5
+
6
+ function writeLine(write, value = '') { write(`${value}\n`); }
7
+ function formatWhen(value) {
8
+ const date = new Date(value);
9
+ return Number.isNaN(date.getTime()) ? String(value || '') : date.toLocaleString();
10
+ }
11
+
12
+ export function renderSessionList(sessions = []) {
13
+ if (!sessions.length) return '\n No resumable Agent Sam sessions found.\n';
14
+ const lines = ['', ' Agent Sam · sessions', ''];
15
+ for (const row of sessions) {
16
+ lines.push(` ${row.id} ${row.title}`);
17
+ lines.push(` ${formatWhen(row.updated_at)} · ${row.model_key || 'no model'} · ${row.cwd}`);
18
+ }
19
+ lines.push('');
20
+ return lines.join('\n');
21
+ }
22
+
23
+ export async function runResume(argv = [], options = {}) {
24
+ const write = options.write || ((text) => process.stdout.write(text));
25
+ const json = argv.includes('--json');
26
+ const listOnly = argv.includes('--list');
27
+ const positional = argv.filter((arg) => !arg.startsWith('-'));
28
+ const unknown = argv.filter((arg) => arg.startsWith('-') && !['--json', '--list'].includes(arg));
29
+ if (unknown.length) throw new Error(`unknown resume option: ${unknown[0]}`);
30
+ if (positional.length > 1) throw new Error('agentsam resume accepts at most one session id');
31
+
32
+ if (listOnly || (json && !positional[0])) {
33
+ const sessions = listLocalSessions({ home: options.home, limit: 30 });
34
+ if (json) writeLine(write, JSON.stringify(sessions, null, 2));
35
+ else write(renderSessionList(sessions));
36
+ return sessions;
37
+ }
38
+
39
+ let sessionId = positional[0] || '';
40
+ if (!sessionId) {
41
+ const sessions = listLocalSessions({ home: options.home, limit: 30 });
42
+ if (!sessions.length) { write(renderSessionList([])); return null; }
43
+ const interactive = options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
44
+ if (!interactive) { write(renderSessionList(sessions)); return null; }
45
+ const choice = await select({
46
+ message: 'Resume Agent Sam session',
47
+ options: sessions.map((row) => ({
48
+ value: row.id,
49
+ label: row.title,
50
+ hint: `${path.basename(row.cwd)} · ${formatWhen(row.updated_at)}`,
51
+ })),
52
+ });
53
+ if (isCancel(choice)) return null;
54
+ sessionId = choice;
55
+ }
56
+
57
+ const session = loadLocalSession(sessionId, { home: options.home });
58
+ if (!session) throw new Error(`session_not_found:${sessionId}`);
59
+ if (json) { writeLine(write, JSON.stringify(session, null, 2)); return session; }
60
+
61
+ writeLine(write, `\n Resuming ${session.id}`);
62
+ writeLine(write, ` ${session.title}`);
63
+ writeLine(write, ` ${session.cwd}\n`);
64
+ const runner = options.runShellImpl || runShell;
65
+ await runner([], { cwd: session.cwd, intro: 'quiet', session, home: options.home });
66
+ return session;
67
+ }
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import { scanProjectSecurity, reportExitCode, repairProject, formatSecurityReport } from '../security/index.js';
3
3
  import { runProcess } from '../security/process.js';
4
+ import { diagnosticFromError, renderDiagnosticError } from '../errors/index.js';
4
5
 
5
6
  const help = [
6
7
  'Agent Sam · security',
@@ -63,9 +64,10 @@ export async function runSecurity(argv) {
63
64
  }
64
65
  process.stdout.write(options.json ? JSON.stringify(report, null, 2) + '\n' : formatSecurityReport(report, { color: Boolean(process.stdout.isTTY && !process.env.NO_COLOR) }));
65
66
  process.exitCode = reportExitCode(report);
66
- } catch {
67
- const report = { schema_version: 1, ok: false, complete: false, status: 'error', error: 'Security operation failed. Check command options, manifest/log validity, network access, and Git prerequisites. Use --help.' };
68
- process.stdout.write(options.json ? JSON.stringify(report) + '\n' : report.error + '\n');
67
+ } catch (error) {
68
+ const diagnostic = diagnosticFromError(error, { source: 'security', kind: 'security_operation_error' });
69
+ const report = { schema_version: 1, ok: false, complete: false, status: 'error', error: diagnostic };
70
+ process.stdout.write(options.json ? JSON.stringify(report) + '\n' : `${renderDiagnosticError(error)}\n`);
69
71
  process.exitCode = 2;
70
72
  }
71
73
  }