@inneranimalmedia/agentsam-sdk 2.1.0 → 2.2.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 (50) hide show
  1. package/README.md +27 -15
  2. package/docs/CAPABILITIES.md +93 -0
  3. package/docs/CLI_SHELL.md +10 -1
  4. package/docs/RELEASES.md +2 -1
  5. package/docs/portable-knowledge.md +4 -4
  6. package/package.json +7 -1
  7. package/packages/identity/package.json +1 -1
  8. package/packages/identity/src/frontend/auth-portal/README.md +1 -1
  9. package/packages/identity/src/index.js +2 -0
  10. package/protocol/capabilities/capability-manifest.schema.json +36 -0
  11. package/protocol/capabilities/manifest.json +179 -0
  12. package/protocol/capabilities/repository-audit-input.schema.json +14 -0
  13. package/protocol/capabilities/repository-audit.schema.json +30 -0
  14. package/protocol/capabilities/repository-snapshot-input.schema.json +11 -0
  15. package/protocol/capabilities/repository-snapshot.schema.json +20 -0
  16. package/protocol/knowledge/chunk.schema.json +15 -73
  17. package/protocol/knowledge/document.schema.json +10 -48
  18. package/protocol/knowledge/index-config.schema.json +2 -2
  19. package/protocol/knowledge/repository.schema.json +11 -52
  20. package/protocol/knowledge/retrieval-query.schema.json +13 -63
  21. package/protocol/knowledge/source.schema.json +9 -43
  22. package/protocol/presets/catalog.json +48 -0
  23. package/python/agentsam_sdk/knowledge/models.py +19 -7
  24. package/python/agentsam_sdk/repository/__main__.py +2 -2
  25. package/python/tests/test_knowledge_models.py +6 -2
  26. package/src/agent/capability-adapter.js +50 -0
  27. package/src/agent/index.js +2 -0
  28. package/src/agent/repository-audit.js +188 -0
  29. package/src/capabilities/index.js +7 -0
  30. package/src/capabilities/manifest.js +22 -0
  31. package/src/capabilities/repository-snapshot.js +180 -0
  32. package/src/cli.js +56 -39
  33. package/src/commands/deploy.js +0 -1
  34. package/src/commands/knowledge.js +5 -6
  35. package/src/commands/product.js +119 -0
  36. package/src/commands/shell.js +253 -0
  37. package/src/index.js +9 -0
  38. package/src/knowledge/config.js +12 -6
  39. package/src/knowledge/contracts.js +1 -1
  40. package/src/knowledge/engine.js +1 -1
  41. package/src/knowledge/service/server.js +2 -2
  42. package/src/lib/git-context.js +3 -1
  43. package/src/lib/slash-commands.js +2 -1
  44. package/src/presets/index.js +20 -0
  45. package/src/repository/index.js +4 -0
  46. package/test/agent-capabilities.test.mjs +67 -0
  47. package/test/capabilities.test.mjs +84 -0
  48. package/test/portable-context.test.mjs +11 -1
  49. package/test/shell.test.mjs +60 -0
  50. package/test/smoke.mjs +2 -2
@@ -0,0 +1,84 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { execFileSync } from 'node:child_process';
7
+ import { getCapability, getCapabilityManifest, repositorySnapshot } from '../src/capabilities/index.js';
8
+ import { getPreset, resolvePreset } from '../src/presets/index.js';
9
+
10
+ const CLI = path.resolve('src/cli.js');
11
+
12
+ function git(cwd, args) { return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); }
13
+
14
+ function fixture() {
15
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-capability-'));
16
+ fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({ name: 'snapshot-fixture', version: '1.0.0', type: 'module' }, null, 2));
17
+ fs.mkdirSync(path.join(root, 'src'));
18
+ fs.writeFileSync(path.join(root, 'src', 'index.js'), 'export const value = 1;\n');
19
+ git(root, ['init', '-q']);
20
+ git(root, ['config', 'user.email', 'test@example.com']);
21
+ git(root, ['config', 'user.name', 'AgentSam Test']);
22
+ git(root, ['add', '.']);
23
+ git(root, ['commit', '-qm', 'fixture']);
24
+ return root;
25
+ }
26
+
27
+ test('capability manifest exposes deterministic primitives without requiring a model', () => {
28
+ const manifest = getCapabilityManifest();
29
+ assert.equal(manifest.schema_version, 1);
30
+ assert.equal(getCapability('repository.snapshot').model_required, false);
31
+ assert.equal(getCapability('repository.snapshot').side_effects, 'none');
32
+ assert.equal(getCapability('knowledge.index').deterministic, true);
33
+ });
34
+
35
+ test('presets resolve to explicit feature/capability selections', () => {
36
+ assert.equal(getPreset('cms').lane, 'cms');
37
+ const preset = resolvePreset('prototype');
38
+ assert.deepEqual(preset.features, ['agent']);
39
+ assert.throws(() => resolvePreset('nope'), /unknown_preset/);
40
+ });
41
+
42
+ test('repository.snapshot composes deterministic evidence and content addressing', async t => {
43
+ const root = fixture();
44
+ t.after(() => fs.rmSync(root, { recursive: true, force: true }));
45
+ const one = await repositorySnapshot({ cwd: root, churnDays: 30 });
46
+ const two = await repositorySnapshot({ cwd: root, churnDays: 30 });
47
+ assert.equal(one.capability, 'repository.snapshot');
48
+ assert.match(one.snapshot_id, /^rsnap_[a-f0-9]{24}$/);
49
+ assert.match(one.content_hash, /^sha256:[a-f0-9]{64}$/);
50
+ assert.equal(one.content_hash, two.content_hash);
51
+ assert.equal(one.snapshot_id, two.snapshot_id);
52
+ assert.equal(one.repository.revision_sha, git(root, ['rev-parse', 'HEAD']));
53
+ assert.ok(one.tree.merkle_root);
54
+ assert.ok(one.intelligence.summary.file_count >= 2);
55
+ assert.equal(one.packages[0].name, 'snapshot-fixture');
56
+ assert.equal(one.knowledge.configured, false);
57
+ });
58
+
59
+ test('product UX creates a preset project, adds a feature, and inspects before first commit', t => {
60
+ const parent = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-product-'));
61
+ t.after(() => fs.rmSync(parent, { recursive: true, force: true }));
62
+ execFileSync(process.execPath, [CLI, 'create', 'demo', '--preset', 'cms', '--target', 'local'], {
63
+ cwd: parent,
64
+ stdio: 'pipe',
65
+ });
66
+ const project = path.join(parent, 'demo');
67
+ const config = JSON.parse(fs.readFileSync(path.join(project, '.agentsam', 'config.json'), 'utf8'));
68
+ assert.equal(config.preset, 'cms');
69
+ assert.equal(config.lane, 'cms');
70
+ assert.deepEqual(config.features, ['cms', 'knowledge']);
71
+ assert.ok(config.capabilities.includes('repository.snapshot'));
72
+
73
+ execFileSync(process.execPath, [CLI, 'add', 'knowledge', '--cwd', project, '--json'], { stdio: 'pipe' });
74
+ const features = JSON.parse(fs.readFileSync(path.join(project, '.agentsam', 'features.json'), 'utf8'));
75
+ assert.equal(features.features.knowledge.selected, true);
76
+
77
+ const snapshot = JSON.parse(execFileSync(process.execPath, [CLI, 'inspect', '--cwd', project, '--json'], {
78
+ encoding: 'utf8',
79
+ stdio: ['ignore', 'pipe', 'pipe'],
80
+ }));
81
+ assert.equal(snapshot.capability, 'repository.snapshot');
82
+ assert.equal(snapshot.repository.revision_sha, null);
83
+ assert.ok(snapshot.tree.stats.files > 0);
84
+ });
@@ -1,7 +1,7 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { execFileSync } from 'node:child_process';
4
- import { mkdtempSync, writeFileSync } from 'node:fs';
4
+ import { mkdtempSync, realpathSync, writeFileSync } from 'node:fs';
5
5
  import { tmpdir } from 'node:os';
6
6
  import { join } from 'node:path';
7
7
 
@@ -39,6 +39,16 @@ test('resolveGitContext derives repository identity from Git without user/worksp
39
39
  assert.equal('userId' in ctx, false);
40
40
  });
41
41
 
42
+ test('resolveGitContext supports an initialized repository before its first commit', () => {
43
+ const root = mkdtempSync(join(tmpdir(), 'agentsam-sdk-unborn-'));
44
+ execFileSync('git', ['init'], { cwd: root, stdio: 'ignore' });
45
+ writeFileSync(join(root, 'README.md'), '# unborn\n');
46
+ const ctx = resolveGitContext({ cwd: root });
47
+ assert.equal(ctx.root, realpathSync(root));
48
+ assert.equal(ctx.revisionSha, null);
49
+ assert.equal(ctx.dirty, true);
50
+ });
51
+
42
52
  test('bridge headers authenticate only the machine principal', () => {
43
53
  const headers = buildBridgeHeaders({
44
54
  env: {
@@ -0,0 +1,60 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import test from 'node:test';
7
+ import { dispatchShellLine, renderShellCatalog, tokenizeShellLine } from '../src/commands/shell.js';
8
+
9
+ const repoRoot = path.resolve(new URL('..', import.meta.url).pathname);
10
+
11
+ test('shell tokenizer preserves Windows paths and quoted arguments', () => {
12
+ assert.deepEqual(tokenizeShellLine('/cd C:\\Users\\conno\\fuelnreetime'), ['/cd', 'C:\\Users\\conno\\fuelnreetime']);
13
+ assert.deepEqual(tokenizeShellLine('/cd "C:\\Users\\Connor Smith\\repo"'), ['/cd', 'C:\\Users\\Connor Smith\\repo']);
14
+ assert.deepEqual(tokenizeShellLine('/agent "inspect this repo"'), ['/agent', 'inspect this repo']);
15
+ });
16
+
17
+ test('shell catalog advertises commands that the REPL owns', () => {
18
+ const catalog = renderShellCatalog();
19
+ assert.match(catalog, /\/help\s+Show Agent Sam commands/);
20
+ assert.match(catalog, /\/status\s+Local project/);
21
+ assert.match(catalog, /\/exit\s+Exit Agent Sam shell/);
22
+ });
23
+
24
+ test('dispatch handles help, pwd, cd, and exit without falling through to host shell', async () => {
25
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-shell-'));
26
+ const child = path.join(root, 'child folder');
27
+ fs.mkdirSync(child);
28
+ let output = '';
29
+ const state = { cwd: root, write: (text) => { output += text; } };
30
+
31
+ let result = await dispatchShellLine('/help', state);
32
+ assert.equal(result.handled, true);
33
+ assert.equal(result.exit, false);
34
+ assert.match(output, /Slash commands/);
35
+
36
+ output = '';
37
+ result = await dispatchShellLine('/pwd', state);
38
+ assert.equal(result.handled, true);
39
+ assert.equal(output.trim(), root);
40
+
41
+ output = '';
42
+ result = await dispatchShellLine('/cd "child folder"', state);
43
+ assert.equal(result.handled, true);
44
+ assert.equal(state.cwd, child);
45
+ assert.equal(output.trim(), child);
46
+
47
+ result = await dispatchShellLine('/exit', state);
48
+ assert.equal(result.exit, true);
49
+ });
50
+
51
+ test('CLI supports a deterministic one-shot slash command for regression tests', () => {
52
+ const result = spawnSync(process.execPath, ['src/cli.js', 'shell', '--command', '/help'], {
53
+ cwd: repoRoot,
54
+ encoding: 'utf8',
55
+ });
56
+ assert.equal(result.status, 0, result.stderr);
57
+ assert.match(result.stdout, /Agent Sam Terminal/);
58
+ assert.match(result.stdout, /\/help/);
59
+ assert.match(result.stdout, /\/exit/);
60
+ });
package/test/smoke.mjs CHANGED
@@ -57,8 +57,8 @@ assert.ok(SLASH_COMMANDS.some((c) => c.cmd === '/db'));
57
57
  assert.ok(SLASH_COMMANDS.some((c) => c.cmd === '/agent'));
58
58
  assert.deepEqual(
59
59
  listSlashCommands({ lane: 'deploy' }).map(({ cmd }) => cmd),
60
- ['/help', '/deploy'],
61
- 'deploy lane keeps global help and excludes commands from other lanes',
60
+ ['/help', '/deploy', '/exit'],
61
+ 'deploy lane keeps global shell controls and excludes commands from other lanes',
62
62
  );
63
63
 
64
64
  printContextSummary({