@inneranimalmedia/agentsam-sdk 2.6.0 → 2.6.2

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 (145) hide show
  1. package/docs/AUTH_IDENTITY_CONTRACT.md +25 -27
  2. package/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
  3. package/docs/RELEASES.md +7 -7
  4. package/docs/SDK_WORKER.md +4 -3
  5. package/docs/SOURCE_ARCHITECTURE.md +58 -0
  6. package/docs/TEST_TIERS.md +26 -0
  7. package/migrations/runtime/0001_cli_runtime.sql +298 -0
  8. package/package.json +28 -7
  9. package/packages/agentsam-repository/README.md +15 -0
  10. package/packages/agentsam-repository/package.json +25 -0
  11. package/packages/agentsam-repository/src/contracts.js +113 -0
  12. package/packages/agentsam-repository/src/index.js +3 -0
  13. package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
  14. package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
  15. package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
  16. package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
  17. package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
  18. package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
  19. package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
  20. package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
  21. package/packages/identity/package.json +2 -2
  22. package/packages/identity/src/contracts/auth-config.js +23 -40
  23. package/packages/identity/tests/auth-config.test.mjs +14 -23
  24. package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
  25. package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
  26. package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
  27. package/protocol/repository/repository-contract.schema.json +24 -0
  28. package/protocol/repository/repository-dependency.schema.json +24 -0
  29. package/protocol/repository/repository-identity.schema.json +17 -0
  30. package/protocol/rpc/v1/common.proto +16 -0
  31. package/protocol/rpc/v1/errors.proto +35 -0
  32. package/protocol/rpc/v1/knowledge.proto +77 -0
  33. package/services/knowledge/package-lock.json +333 -0
  34. package/services/knowledge/package.json +5 -1
  35. package/src/agent/responses-runner.js +63 -35
  36. package/src/capabilities/repository-snapshot.js +3 -3
  37. package/src/cli.js +36 -29
  38. package/src/commands/account-auth.js +1 -1
  39. package/src/commands/context-economics.js +17 -2
  40. package/src/commands/context.js +1 -1
  41. package/src/commands/db.js +20 -3
  42. package/src/commands/deploy.js +2 -2
  43. package/src/commands/env.js +90 -0
  44. package/src/commands/knowledge.js +12 -4
  45. package/src/commands/merkle-persist.js +30 -11
  46. package/src/commands/merkle.js +1 -1
  47. package/src/commands/models.js +123 -65
  48. package/src/commands/ollama.js +26 -0
  49. package/src/commands/preferences.js +53 -26
  50. package/src/commands/providers.js +308 -0
  51. package/src/commands/shell.js +240 -48
  52. package/src/commands/tunnel.js +8 -8
  53. package/src/commands/whoami.js +42 -21
  54. package/src/errors/contract.js +236 -0
  55. package/src/errors/diagnostic.js +1 -0
  56. package/src/errors/index.js +14 -0
  57. package/src/index.js +13 -1
  58. package/src/knowledge/service/auth.js +13 -0
  59. package/src/knowledge/service/grpc-client.js +115 -0
  60. package/src/knowledge/service/grpc-codec.js +237 -0
  61. package/src/knowledge/service/grpc-server.js +83 -0
  62. package/src/knowledge/service/job-engine.js +248 -0
  63. package/src/knowledge/service/server.js +87 -135
  64. package/src/knowledge/source.js +1 -1
  65. package/src/lib/account-session.js +94 -25
  66. package/src/lib/auth.js +314 -51
  67. package/src/lib/cli-preferences.js +31 -4
  68. package/src/lib/core-client.js +75 -30
  69. package/src/lib/deploy-receipt/index.js +2 -2
  70. package/src/lib/detect-context.js +14 -16
  71. package/src/lib/knowledge-docker.js +6 -3
  72. package/src/lib/local-sessions.js +23 -2
  73. package/src/lib/local-status.js +1 -1
  74. package/src/lib/project-config.js +1 -1
  75. package/src/lib/provider-credentials.js +230 -14
  76. package/src/lib/slash-commands.js +5 -3
  77. package/src/local/migrations.js +93 -0
  78. package/src/local/runtime-store.js +141 -0
  79. package/src/local/sqlite.js +2 -0
  80. package/src/local-pty/server.js +113 -51
  81. package/src/models/discovery.js +327 -0
  82. package/src/providers/anthropic-messages.js +192 -0
  83. package/src/providers/cloudflare-chat.js +183 -0
  84. package/src/providers/factory.js +69 -0
  85. package/src/providers/gemini-generate-content.js +208 -0
  86. package/src/providers/index.js +5 -0
  87. package/src/providers/ollama-chat.js +148 -0
  88. package/src/providers/openai-responses.js +226 -75
  89. package/src/repository/index.js +14 -2
  90. package/src/rpc/generated/common_grpc_pb.js +1 -0
  91. package/src/rpc/generated/common_pb.js +536 -0
  92. package/src/rpc/generated/errors_grpc_pb.js +1 -0
  93. package/src/rpc/generated/errors_pb.js +482 -0
  94. package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
  95. package/src/rpc/generated/knowledge_pb.js +2168 -0
  96. package/src/rpc/generated/package.json +3 -0
  97. package/src/security/trust-boundary.js +2 -2
  98. package/src/telemetry/events.js +4 -1
  99. package/src/ui/cli/activity.js +76 -0
  100. package/src/ui/cli/compaction.js +15 -0
  101. package/src/ui/cli/footer.js +39 -0
  102. package/src/ui/cli/help.js +194 -0
  103. package/src/ui/cli/plan.js +20 -0
  104. package/src/ui/cli/runtime-events.js +110 -0
  105. package/src/ui/cli/waiting.js +16 -0
  106. package/src/ui/merkle/render.js +1 -1
  107. package/test/account-session.test.mjs +48 -11
  108. package/test/apps-scaffold-contract.test.mjs +43 -0
  109. package/test/cli/preferences-runtime.test.mjs +11 -0
  110. package/test/cli/runtime-ui.test.mjs +74 -0
  111. package/test/error-diagnostics.test.mjs +57 -1
  112. package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
  113. package/test/integration/cli-help.test.mjs +37 -0
  114. package/test/integration/knowledge-rpc.test.mjs +112 -0
  115. package/test/integration/merkle-cli.test.mjs +61 -0
  116. package/test/integration/merkle-persistence-identity.test.mjs +48 -0
  117. package/test/integration/provider-env-cli.test.mjs +49 -0
  118. package/test/integration/provider-factory.test.mjs +197 -0
  119. package/test/integration/repository-company-graph.test.mjs +90 -0
  120. package/test/integration/runtime-migrations.test.mjs +82 -0
  121. package/test/knowledge-service.test.mjs +5 -0
  122. package/test/knowledge.test.mjs +16 -0
  123. package/test/live/terminal-transport.live.test.mjs +24 -0
  124. package/test/local-sessions.test.mjs +7 -1
  125. package/test/models.test.mjs +101 -4
  126. package/test/ollama.test.mjs +21 -0
  127. package/test/portable-context.test.mjs +1 -1
  128. package/test/provider-credentials.test.mjs +45 -1
  129. package/test/release-hygiene.test.mjs +13 -5
  130. package/test/responses-runner.test.mjs +3 -1
  131. package/test/sdk-worker-contract.test.mjs +5 -1
  132. package/test/shell.test.mjs +50 -8
  133. package/test/smoke.mjs +2 -1
  134. package/test/terminal/local-pty.mock.test.mjs +151 -0
  135. package/test/whoami-resume.test.mjs +6 -5
  136. package/src/lib/prompt-byok.js +0 -57
  137. package/src/lib/save-sdk-token.js +0 -19
  138. /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
  139. /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
  140. /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
  141. /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
  142. /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
  143. /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
  144. /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
  145. /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
package/package.json CHANGED
@@ -1,23 +1,28 @@
1
1
  {
2
2
  "name": "@inneranimalmedia/agentsam-sdk",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "description": "Portable AgentSam SDK and CLI kits for local scaffolding, repository intelligence, incremental indexing, identity adapters, and verified dependency maintenance.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js",
9
- "./git-context": "./src/lib/git-context.js",
9
+ "./git-context": "./packages/agentsam-repository/src/git-context.js",
10
10
  "./bridge-client": "./src/lib/bridge-client.js",
11
11
  "./local/sqlite": "./src/local/sqlite.js",
12
12
  "./dockerize": "./src/lib/dockerize.js",
13
13
  "./mini": "./src/lib/mini/index.js",
14
- "./merkle": "./src/lib/merkle/index.js",
14
+ "./merkle": "./packages/agentsam-repository/src/merkle/index.js",
15
15
  "./deploy-receipt": "./src/lib/deploy-receipt/index.js",
16
16
  "./knowledge": "./src/knowledge/index.js",
17
17
  "./knowledge-service-client": "./src/knowledge/service/client.js",
18
+ "./knowledge-service-grpc-client": "./src/knowledge/service/grpc-client.js",
18
19
  "./capabilities": "./src/capabilities/index.js",
19
20
  "./capability-manifest": "./protocol/capabilities/manifest.json",
20
21
  "./repository": "./src/repository/index.js",
22
+ "./repository/contracts": "./packages/agentsam-repository/src/contracts.js",
23
+ "./repository/identity-schema": "./protocol/repository/repository-identity.schema.json",
24
+ "./repository/contract-schema": "./protocol/repository/repository-contract.schema.json",
25
+ "./repository/dependency-schema": "./protocol/repository/repository-dependency.schema.json",
21
26
  "./agent": "./src/agent/index.js",
22
27
  "./presets": "./src/presets/index.js",
23
28
  "./preset-catalog": "./protocol/presets/catalog.json",
@@ -71,6 +76,7 @@
71
76
  "files": [
72
77
  "src",
73
78
  "packages/identity",
79
+ "packages/agentsam-repository",
74
80
  "packages/connectors/cloudflare",
75
81
  "templates",
76
82
  "docs",
@@ -78,6 +84,7 @@
78
84
  "test",
79
85
  "python",
80
86
  "protocol",
87
+ "migrations",
81
88
  "skills",
82
89
  "services/knowledge/package.json",
83
90
  "services/knowledge/package-lock.json",
@@ -93,19 +100,28 @@
93
100
  "packages/agentsam-shell-kit",
94
101
  "packages/identity",
95
102
  "packages/agentsam-contracts",
103
+ "packages/agentsam-repository",
96
104
  "packages/agentsam-workbench"
97
105
  ],
98
106
  "scripts": {
99
- "test": "node test/smoke.mjs && node --test test/*.test.mjs && npm --prefix packages/identity test && npm run test:connectors",
107
+ "test": "npm run test:standard && npm --prefix packages/identity test && npm run test:connectors",
108
+ "test:standard": "node test/smoke.mjs && npm run test:unit && npm run test:cli && npm run test:integration:mock && npm run test:terminal:mock",
109
+ "test:unit": "node --test test/*.test.mjs",
110
+ "test:cli": "node --test test/cli/*.test.mjs",
111
+ "test:integration:mock": "node --test test/integration/*.test.mjs",
112
+ "test:terminal:mock": "node --test test/terminal/*.test.mjs",
113
+ "test:live:terminal": "node --test test/live/*.live.test.mjs",
100
114
  "test:identity": "npm --prefix packages/identity test",
115
+ "test:workspaces": "npm --prefix packages/agentsam-contracts test && npm --prefix packages/agentsam-contracts run typecheck && npm --prefix packages/agentsam-repository test && npm --prefix packages/agentsam-workbench test && npm --prefix packages/agentsam-workbench run typecheck && npm --prefix packages/agentsam-shell-kit run typecheck",
101
116
  "smoke": "node test/smoke.mjs",
102
117
  "verify:package": "node scripts/verify-package.mjs",
118
+ "verify:boundaries": "node scripts/verify-source-boundaries.mjs",
103
119
  "verify:bootstrap": "node scripts/verify-bootstrap.mjs",
104
120
  "verify:knowledge-package": "node scripts/verify-knowledge-package.mjs",
105
121
  "test:python": "node scripts/verify-python.mjs",
106
122
  "verify:release": "npm run verify && npm run verify:knowledge-package && npm run test:python && npm run security:check",
107
123
  "pack:check": "npm pack --dry-run --json",
108
- "verify": "npm test && npm run verify:package && npm run verify:bootstrap && npm run pack:check",
124
+ "verify": "npm run proto:check && npm test && npm run test:workspaces && npm run verify:boundaries && npm run verify:package && npm run verify:bootstrap && npm run pack:check",
109
125
  "publish:identity-alpha": "bash scripts/publish-identity-alpha.sh",
110
126
  "publish:identity-alpha:dry-run": "bash scripts/publish-identity-alpha.sh --dry-run",
111
127
  "publish:identity-alpha:publish-only": "bash scripts/publish-identity-alpha.sh --publish-only",
@@ -115,7 +131,9 @@
115
131
  "security:repair": "node src/security/cli.mjs repair",
116
132
  "ui:preview": "node scripts/ui-preview.mjs",
117
133
  "test:connectors": "node --test packages/connectors/cloudflare/tests/**/*.test.mjs",
118
- "verify:npm10-lock": "node apps/local-studio/scripts/verify-npm10-lock.mjs"
134
+ "verify:npm10-lock": "node apps/local-studio/scripts/verify-npm10-lock.mjs",
135
+ "proto:generate": "node scripts/generate-proto.mjs",
136
+ "proto:check": "node scripts/verify-proto.mjs"
119
137
  },
120
138
  "engines": {
121
139
  "node": ">=22 <25",
@@ -123,6 +141,8 @@
123
141
  },
124
142
  "dependencies": {
125
143
  "@clack/prompts": "^1.7.0",
144
+ "@grpc/grpc-js": "^1.14.4",
145
+ "google-protobuf": "^3.21.4",
126
146
  "node-pty": "^1.0.0",
127
147
  "pg": "8.16.3",
128
148
  "picocolors": "^1.1.1",
@@ -156,7 +176,8 @@
156
176
  },
157
177
  "devDependencies": {
158
178
  "@electric-sql/pglite": "0.5.8",
159
- "@electric-sql/pglite-pgvector": "0.0.9"
179
+ "@electric-sql/pglite-pgvector": "0.0.9",
180
+ "grpc-tools": "^1.13.1"
160
181
  },
161
182
  "packageManager": "npm@10.9.2"
162
183
  }
@@ -0,0 +1,15 @@
1
+ # @inneranimalmedia/agentsam-repository
2
+
3
+ Owned repository/runtime package for AgentSam.
4
+
5
+ It owns deterministic repository identity and Merkle behavior that must be reusable independently of the CLI surface:
6
+
7
+ - Git remote/context normalization
8
+ - Merkle content trees and snapshots
9
+ - semantic/file metadata classification
10
+ - Merkle persistence contracts and Cloudflare binding adapters
11
+ - repository graph runtime normalization
12
+
13
+ It does **not** own authenticated account sessions, CLI commands, UI rendering, knowledge retrieval, deployment orchestration, or product application state. Those layers consume this package through explicit imports.
14
+
15
+ The workspace is private while the extraction stabilizes. Cross-repository consumers should continue using the published `@inneranimalmedia/agentsam-sdk` facade until this package receives its own release policy.
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@inneranimalmedia/agentsam-repository",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "Portable AgentSam repository identity, Merkle, semantic file metadata, persistence, and repository graph runtime contracts.",
6
+ "type": "module",
7
+ "main": "./src/index.js",
8
+ "exports": {
9
+ ".": "./src/index.js",
10
+ "./git-context": "./src/git-context.js",
11
+ "./contracts": "./src/contracts.js",
12
+ "./merkle": "./src/merkle/index.js",
13
+ "./merkle/persistence": "./src/merkle/persistence.js",
14
+ "./merkle/cloudflare-persistence": "./src/merkle/cloudflare-persistence.js"
15
+ },
16
+ "scripts": {
17
+ "test": "node --test test/*.test.mjs"
18
+ },
19
+ "dependencies": {
20
+ "typescript": "5.9.3"
21
+ },
22
+ "engines": {
23
+ "node": ">=22 <25"
24
+ }
25
+ }
@@ -0,0 +1,113 @@
1
+ export const COMPANY_REPOSITORY_GRAPH_SCHEMA_VERSION = 1;
2
+
3
+ export const REPOSITORY_STATUSES = Object.freeze(['active', 'archived', 'disabled']);
4
+ export const REPOSITORY_CONTRACT_TYPES = Object.freeze(['api', 'schema', 'runtime', 'cli', 'event', 'receipt', 'package']);
5
+ export const REPOSITORY_CONTRACT_STATUSES = Object.freeze(['active', 'deprecated', 'retired']);
6
+ export const REPOSITORY_DEPENDENCY_TYPES = Object.freeze(['runtime', 'build', 'contract', 'package', 'deploy', 'tool', 'data']);
7
+ export const REPOSITORY_DEPENDENCY_CRITICALITIES = Object.freeze(['informational', 'compatible', 'strict', 'critical']);
8
+ export const REPOSITORY_FAILURE_POLICIES = Object.freeze(['warn', 'block_certification', 'block_deploy', 'degrade']);
9
+
10
+ const LEGACY_OWNERSHIP_KEYS = Object.freeze([
11
+ 'tenant_id', 'tenantId',
12
+ 'workspace_id', 'workspaceId',
13
+ 'user_id', 'userId',
14
+ 'owner_user_id', 'ownerUserId',
15
+ ]);
16
+
17
+ function clean(value) {
18
+ return value == null ? '' : String(value).trim();
19
+ }
20
+
21
+ function required(value, label) {
22
+ const text = clean(value);
23
+ if (!text) throw new TypeError(`${label} is required`);
24
+ return text;
25
+ }
26
+
27
+ function optional(value) {
28
+ const text = clean(value);
29
+ return text || null;
30
+ }
31
+
32
+ function enumValue(value, allowed, label) {
33
+ const text = required(value, label).toLowerCase();
34
+ if (!allowed.includes(text)) throw new RangeError(`unsupported ${label}: ${text}`);
35
+ return text;
36
+ }
37
+
38
+ function optionalUnix(value, label) {
39
+ if (value == null || value === '') return null;
40
+ const number = Number(value);
41
+ if (!Number.isInteger(number) || number < 0) throw new RangeError(`${label} must be a non-negative integer`);
42
+ return number;
43
+ }
44
+
45
+ function rejectLegacyOwnership(value) {
46
+ for (const key of LEGACY_OWNERSHIP_KEYS) {
47
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
48
+ throw new TypeError(`legacy ownership field is not supported: ${key}`);
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Portable repository identity. Ownership is intentionally not embedded here;
55
+ * the authenticated host binds repository_id to account_id in its registry.
56
+ */
57
+ export function createRepositoryIdentity(value = {}) {
58
+ rejectLegacyOwnership(value);
59
+ if (Object.prototype.hasOwnProperty.call(value, 'account_id') || Object.prototype.hasOwnProperty.call(value, 'accountId')) {
60
+ throw new TypeError('repository identity must not embed account_id');
61
+ }
62
+ return Object.freeze({
63
+ schema_version: COMPANY_REPOSITORY_GRAPH_SCHEMA_VERSION,
64
+ repository_id: required(value.repository_id ?? value.repositoryId, 'repository_id'),
65
+ full_name: required(value.full_name ?? value.fullName, 'full_name'),
66
+ role: required(value.role, 'role'),
67
+ default_branch: optional(value.default_branch ?? value.defaultBranch) || 'main',
68
+ canonical_url: optional(value.canonical_url ?? value.canonicalUrl),
69
+ status: enumValue(value.status || 'active', REPOSITORY_STATUSES, 'repository status'),
70
+ });
71
+ }
72
+
73
+ /** Account-bound declaration of one public repository contract. */
74
+ export function createRepositoryContract(value = {}) {
75
+ rejectLegacyOwnership(value);
76
+ return Object.freeze({
77
+ schema_version: COMPANY_REPOSITORY_GRAPH_SCHEMA_VERSION,
78
+ id: required(value.id, 'id'),
79
+ account_id: required(value.account_id ?? value.accountId, 'account_id'),
80
+ repository_id: required(value.repository_id ?? value.repositoryId, 'repository_id'),
81
+ contract_key: required(value.contract_key ?? value.contractKey, 'contract_key'),
82
+ contract_version: required(value.contract_version ?? value.contractVersion, 'contract_version'),
83
+ contract_type: enumValue(value.contract_type ?? value.contractType, REPOSITORY_CONTRACT_TYPES, 'contract_type'),
84
+ name: optional(value.name) || required(value.contract_key ?? value.contractKey, 'contract_key'),
85
+ description: optional(value.description),
86
+ manifest_path: optional(value.manifest_path ?? value.manifestPath),
87
+ contract_hash: required(value.contract_hash ?? value.contractHash, 'contract_hash'),
88
+ status: enumValue(value.status || 'active', REPOSITORY_CONTRACT_STATUSES, 'contract status'),
89
+ created_at: optionalUnix(value.created_at ?? value.createdAt, 'created_at'),
90
+ updated_at: optionalUnix(value.updated_at ?? value.updatedAt, 'updated_at'),
91
+ });
92
+ }
93
+
94
+ /** Account-bound cross-repository dependency edge. */
95
+ export function createRepositoryDependency(value = {}) {
96
+ rejectLegacyOwnership(value);
97
+ return Object.freeze({
98
+ schema_version: COMPANY_REPOSITORY_GRAPH_SCHEMA_VERSION,
99
+ id: required(value.id, 'id'),
100
+ account_id: required(value.account_id ?? value.accountId, 'account_id'),
101
+ source_repository_id: required(value.source_repository_id ?? value.sourceRepositoryId, 'source_repository_id'),
102
+ target_repository_id: required(value.target_repository_id ?? value.targetRepositoryId, 'target_repository_id'),
103
+ dependency_type: enumValue(value.dependency_type ?? value.dependencyType, REPOSITORY_DEPENDENCY_TYPES, 'dependency_type'),
104
+ contract_id: optional(value.contract_id ?? value.contractId),
105
+ criticality: enumValue(value.criticality, REPOSITORY_DEPENDENCY_CRITICALITIES, 'criticality'),
106
+ required_version: optional(value.required_version ?? value.requiredVersion),
107
+ required_contract_hash: optional(value.required_contract_hash ?? value.requiredContractHash),
108
+ failure_policy: enumValue(value.failure_policy ?? value.failurePolicy, REPOSITORY_FAILURE_POLICIES, 'failure_policy'),
109
+ description: optional(value.description),
110
+ created_at: optionalUnix(value.created_at ?? value.createdAt, 'created_at'),
111
+ updated_at: optionalUnix(value.updated_at ?? value.updatedAt, 'updated_at'),
112
+ });
113
+ }
@@ -0,0 +1,3 @@
1
+ export { resolveGitContext, tryResolveGitContext, normalizeGitRemote } from './git-context.js';
2
+ export * from './contracts.js';
3
+ export * from './merkle/index.js';
@@ -129,25 +129,15 @@ export function resolveWranglerMerklePersistence({
129
129
  };
130
130
  }
131
131
 
132
- function providerRepoId(git) {
133
- const host = clean(git?.remoteHost).toLowerCase();
134
- const fullName = clean(git?.repoFullName);
135
- if (!fullName) return null;
136
- if (host === 'github.com') return `github:${fullName}`;
137
- if (host === 'gitlab.com') return `gitlab:${fullName}`;
138
- if (host === 'bitbucket.org') return `bitbucket:${fullName}`;
139
- return null;
140
- }
141
-
142
132
  function safeId(value) {
143
133
  return clean(value).replace(/[^A-Za-z0-9._-]+/g, '').slice(0, 96);
144
134
  }
145
135
 
146
- function snapshotIdFor({ snapshot, repoId, captureKind, deploymentId }) {
136
+ function snapshotIdFor({ snapshot, repositoryId, captureKind, deploymentId }) {
147
137
  const deployment = safeId(deploymentId);
148
138
  if (captureKind === 'deploy' && deployment) return `mrs_dep_${deployment.toLowerCase()}`;
149
139
  const digest = createHash('sha256')
150
- .update([repoId, snapshot.rootHash, snapshot.semantic?.rootHash || '', captureKind].join('\0'))
140
+ .update([repositoryId, snapshot.rootHash, snapshot.semantic?.rootHash || '', captureKind].join('\0'))
151
141
  .digest('hex');
152
142
  return `mrs_${digest.slice(0, 24)}`;
153
143
  }
@@ -167,8 +157,8 @@ function sqlInt(value) {
167
157
  export function buildMerklePersistencePlan({
168
158
  snapshot,
169
159
  root = process.cwd(),
170
- ownerUserId,
171
- repoId,
160
+ accountId,
161
+ repositoryId,
172
162
  source = 'local',
173
163
  captureKind = 'manual',
174
164
  connectionId = null,
@@ -180,24 +170,24 @@ export function buildMerklePersistencePlan({
180
170
  wrangler,
181
171
  } = {}) {
182
172
  if (!snapshot?.rootHash || !Array.isArray(snapshot?.entries)) throw new Error('merkle_snapshot_required');
183
- const owner = clean(ownerUserId);
184
- if (!owner) throw new Error('owner_user_id_required');
173
+ const account = clean(accountId);
174
+ const repository = clean(repositoryId);
175
+ if (!account) throw new Error('account_id_required');
176
+ if (!repository) throw new Error('repository_id_required');
185
177
  if (!CAPTURE_KINDS.has(captureKind)) throw new Error(`capture_kind_invalid:${captureKind}`);
186
178
  if (!SOURCES.has(source)) throw new Error(`source_invalid:${source}`);
187
179
  if (captureKind !== 'deploy' && !clean(connectionId) && !clean(runtimeLeaseId)) throw new Error('execution_provenance_required');
188
180
  let git = null;
189
181
  try { git = resolveGitContext({ cwd: root }); } catch { git = null; }
190
- const resolvedRepoId = clean(repoId) || providerRepoId(git);
191
- if (!resolvedRepoId) throw new Error('repo_id_required');
192
- const snapshotId = snapshotIdFor({ snapshot, repoId: resolvedRepoId, captureKind, deploymentId });
182
+ const snapshotId = snapshotIdFor({ snapshot, repositoryId: repository, captureKind, deploymentId });
193
183
  const prefix = normalizeMerkleStoragePrefix(storagePrefix);
194
- const storageKey = merkleSnapshotStorageKey({ ownerUserId: owner, repoId: resolvedRepoId, snapshotId, prefix });
184
+ const storageKey = merkleSnapshotStorageKey({ accountId: account, repositoryId: repository, snapshotId, prefix });
195
185
  const createdAt = Math.floor(Date.now() / 1000);
196
186
  const classifier = snapshot.semantic?.classifier || null;
197
187
  const row = {
198
188
  snapshot_id: snapshotId,
199
- owner_user_id: owner,
200
- repo_id: resolvedRepoId,
189
+ account_id: account,
190
+ repository_id: repository,
201
191
  repository: git?.remoteUrl || null,
202
192
  source,
203
193
  manifest_format: snapshot.format || 'agentsam-merkle',
@@ -235,7 +225,7 @@ export function buildMerklePersistencePlan({
235
225
 
236
226
  export function merklePersistenceUpsertSql(row) {
237
227
  return `INSERT INTO ${MERKLE_SNAPSHOT_TABLE} (
238
- snapshot_id, owner_user_id, repo_id, repository, source,
228
+ snapshot_id, account_id, repository_id, repository, source,
239
229
  manifest_format, manifest_version, hash_algorithm, root_hash, policy_hash,
240
230
  resolved_commit_sha, resolved_tree_sha, git_branch, working_tree_dirty,
241
231
  connection_id, runtime_lease_id, storage_backend, storage_bucket, storage_key,
@@ -243,7 +233,7 @@ export function merklePersistenceUpsertSql(row) {
243
233
  capture_kind, deployment_id, worker_version_id, reference_label,
244
234
  created_at, persisted_at, metadata_root, classifier_format, classifier_version, classifier_source
245
235
  ) VALUES (
246
- ${sqlText(row.snapshot_id)}, ${sqlText(row.owner_user_id)}, ${sqlText(row.repo_id)}, ${sqlText(row.repository)}, ${sqlText(row.source)},
236
+ ${sqlText(row.snapshot_id)}, ${sqlText(row.account_id)}, ${sqlText(row.repository_id)}, ${sqlText(row.repository)}, ${sqlText(row.source)},
247
237
  ${sqlText(row.manifest_format)}, ${sqlInt(row.manifest_version)}, ${sqlText(row.hash_algorithm)}, ${sqlText(row.root_hash)}, ${sqlText(row.policy_hash)},
248
238
  ${sqlText(row.resolved_commit_sha)}, ${sqlText(row.resolved_tree_sha)}, ${sqlText(row.git_branch)}, ${sqlInt(row.working_tree_dirty)},
249
239
  ${sqlText(row.connection_id)}, ${sqlText(row.runtime_lease_id)}, ${sqlText(row.storage_backend)}, ${sqlText(row.storage_bucket)}, ${sqlText(row.storage_key)},
@@ -10,6 +10,7 @@ export async function buildSemanticMetadata(...args) {
10
10
  export { validateSemanticMetadata, metadataRoot, FILEMETA_FORMAT, FILEMETA_VERSION } from './filemeta.js';
11
11
 
12
12
  export {
13
+ MERKLE_PERSISTENCE_SCHEMA_VERSION,
13
14
  MERKLE_SNAPSHOT_SCHEMA_SQL,
14
15
  MERKLE_SNAPSHOT_STORAGE_PREFIX,
15
16
  MERKLE_SNAPSHOT_TABLE,
@@ -27,15 +27,17 @@ export function normalizeMerkleStoragePrefix(value = MERKLE_SNAPSHOT_STORAGE_PRE
27
27
  * Provider-neutral object key. The host chooses which physical bucket is bound
28
28
  * to the logical WEBSITE_ASSETS role; the SDK never owns cloud credentials.
29
29
  */
30
- export function merkleSnapshotStorageKey({ ownerUserId, repoId, snapshotId, prefix = MERKLE_SNAPSHOT_STORAGE_PREFIX } = {}) {
31
- return `${normalizeMerkleStoragePrefix(prefix)}/${safeSegment(ownerUserId, 'owner_user_id')}/${safeSegment(repoId, 'repo_id')}/${safeSegment(snapshotId, 'snapshot_id')}.json`;
30
+ export const MERKLE_PERSISTENCE_SCHEMA_VERSION = 2;
31
+
32
+ export function merkleSnapshotStorageKey({ accountId, repositoryId, snapshotId, prefix = MERKLE_SNAPSHOT_STORAGE_PREFIX } = {}) {
33
+ return `${normalizeMerkleStoragePrefix(prefix)}/${safeSegment(accountId, 'account_id')}/${safeSegment(repositoryId, 'repository_id')}/${safeSegment(snapshotId, 'snapshot_id')}.json`;
32
34
  }
33
35
 
34
36
  /** Portable D1/SQLite schema for hosts that opt into persisted Merkle snapshots. */
35
37
  export const MERKLE_SNAPSHOT_SCHEMA_SQL = `CREATE TABLE IF NOT EXISTS ${MERKLE_SNAPSHOT_TABLE} (
36
38
  snapshot_id TEXT PRIMARY KEY NOT NULL,
37
- owner_user_id TEXT NOT NULL,
38
- repo_id TEXT NOT NULL,
39
+ account_id TEXT NOT NULL,
40
+ repository_id TEXT NOT NULL,
39
41
  repository TEXT,
40
42
  source TEXT NOT NULL CHECK (source IN ('github','gitlab','bitbucket','local','upload')),
41
43
  manifest_format TEXT NOT NULL DEFAULT 'agentsam-merkle',
@@ -2,6 +2,7 @@ import { comparePaths } from './hash.js';
2
2
 
3
3
  export const DEFAULT_IGNORES = Object.freeze([
4
4
  '.git', 'node_modules', 'dist', '.DS_Store', '.agentsam/cache', '.agentsam/merkle', '.agentsam/merkle.json',
5
+ '.agentsam/backups', '.agentsam/intelligence/archive',
5
6
  ]);
6
7
  export function validPath(value, allowRoot = false) {
7
8
  return typeof value === 'string' && ((allowRoot && value === '') || (value.length > 0 &&
@@ -0,0 +1,40 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import {
4
+ createRepositoryContract,
5
+ createRepositoryDependency,
6
+ createRepositoryIdentity,
7
+ } from '../src/contracts.js';
8
+
9
+ test('repository package owns account/repository graph normalization', () => {
10
+ const identity = createRepositoryIdentity({
11
+ repository_id: 'github:owner/repo',
12
+ full_name: 'owner/repo',
13
+ role: 'runtime',
14
+ });
15
+ assert.equal(identity.repository_id, 'github:owner/repo');
16
+ assert.equal(Object.hasOwn(identity, 'account_id'), false);
17
+
18
+ const contract = createRepositoryContract({
19
+ id: 'contract:runtime:v1',
20
+ account_id: 'au_test',
21
+ repository_id: identity.repository_id,
22
+ contract_key: 'runtime',
23
+ contract_version: '1',
24
+ contract_type: 'runtime',
25
+ contract_hash: `sha256:${'1'.repeat(64)}`,
26
+ });
27
+ assert.equal(contract.account_id, 'au_test');
28
+
29
+ const dependency = createRepositoryDependency({
30
+ id: 'dep:a:b',
31
+ account_id: 'au_test',
32
+ source_repository_id: 'github:owner/a',
33
+ target_repository_id: 'github:owner/b',
34
+ dependency_type: 'contract',
35
+ criticality: 'strict',
36
+ failure_policy: 'block_certification',
37
+ });
38
+ assert.equal(dependency.failure_policy, 'block_certification');
39
+ assert.throws(() => createRepositoryContract({ ...contract, workspace_id: 'ws_legacy' }), /legacy ownership field/);
40
+ });
@@ -0,0 +1,24 @@
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 { execFileSync } from 'node:child_process';
6
+ import test from 'node:test';
7
+ import { normalizeGitRemote, resolveGitContext } from '../src/git-context.js';
8
+
9
+ test('repository package derives resource identity from Git, not account environment', t => {
10
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-repository-git-'));
11
+ t.after(() => fs.rmSync(root, { recursive: true, force: true }));
12
+ execFileSync('git', ['init', '-q', root]);
13
+ execFileSync('git', ['-C', root, 'remote', 'add', 'origin', 'git@github.com:ExampleOrg/DemoRepo.git']);
14
+ const context = resolveGitContext({ cwd: root });
15
+ assert.equal(context.repoFullName, 'ExampleOrg/DemoRepo');
16
+ assert.equal(context.owner, 'ExampleOrg');
17
+ assert.equal(context.repo, 'DemoRepo');
18
+ assert.equal(context.revisionSha, null);
19
+ });
20
+
21
+ test('remote normalization supports HTTPS and SSH without ownership fields', () => {
22
+ assert.equal(normalizeGitRemote('https://github.com/Owner/Repo.git').repoFullName, 'Owner/Repo');
23
+ assert.equal(normalizeGitRemote('git@github.com:Owner/Repo.git').repoFullName, 'Owner/Repo');
24
+ });
@@ -3,11 +3,7 @@ import { test } from 'node:test';
3
3
  import fs from 'node:fs/promises';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
- import { spawnSync } from 'node:child_process';
7
- import { fileURLToPath } from 'node:url';
8
- import { buildMerkleTree, saveSnapshot, readSnapshot, validateSnapshot, diffTrees, metadataRoot } from '../src/lib/merkle/index.js';
9
-
10
- const cli = fileURLToPath(new URL('../src/cli.js', import.meta.url));
6
+ import { buildMerkleTree, saveSnapshot, readSnapshot, validateSnapshot, diffTrees, metadataRoot } from '../src/merkle/index.js';
11
7
  async function fixture(t) {
12
8
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'agentsam-merkle-test-'));
13
9
  t.after(() => fs.rm(root, { recursive: true, force: true }));
@@ -17,8 +13,6 @@ async function write(root, name, content = name) {
17
13
  await fs.mkdir(path.dirname(path.join(root, name)), { recursive: true });
18
14
  await fs.writeFile(path.join(root, name), content);
19
15
  }
20
- function run(args, cwd) { return spawnSync(process.execPath, [cli, 'merkle', ...args], { cwd, encoding: 'utf8', timeout: 15000 }); }
21
-
22
16
  test('version 1 hashes match independent SHA-256 protocol vectors', async (t) => {
23
17
  const root = await fixture(t);
24
18
  assert.equal((await buildMerkleTree(root)).rootHash, 'sha256:6ce8ca443f3cf6c719c5cb9acc121403addf3f499eefa3db25edacf2c7fe0f94');
@@ -49,7 +43,7 @@ test('default ignores, explicit dist inclusion, literal exclusions, and empty-di
49
43
  const root = await fixture(t);
50
44
  await write(root, 'src/app.js');
51
45
  const first = await buildMerkleTree(root);
52
- for (const name of ['.git/config', 'node_modules/pkg/file', 'dist/app.js', '.DS_Store', '.agentsam/cache/data', '.agentsam/merkle/old.json']) await write(root, name);
46
+ for (const name of ['.git/config', 'node_modules/pkg/file', 'dist/app.js', '.DS_Store', '.agentsam/cache/data', '.agentsam/merkle/old.json', '.agentsam/backups/pre-cleanup.bak', '.agentsam/intelligence/archive/20260912/repo_intelligence.json']) await write(root, name);
53
47
  await fs.mkdir(path.join(root, 'empty'));
54
48
  assert.equal(first.rootHash, (await buildMerkleTree(root)).rootHash);
55
49
  const withDist = await buildMerkleTree(root, { include: ['dist'] });
@@ -172,30 +166,6 @@ test('scan fails when a file disappears and honors cancellation', async (t) => {
172
166
  await assert.rejects(buildMerkleTree(root, { signal: controller.signal }), { name: 'AbortError' });
173
167
  });
174
168
 
175
- test('CLI snapshot/verify/diff supports JSON, moved roots, and distinct mismatch/error exit codes', async (t) => {
176
- const root = await fixture(t), moved = await fixture(t);
177
- await write(root, 'a.txt', 'hello'); await write(moved, 'a.txt', 'hello');
178
- const saved = run(['snapshot', '.', '--json'], root);
179
- assert.equal(saved.status, 0, saved.stderr);
180
- const manifest = JSON.parse(saved.stdout);
181
- assert.ok(manifest.output.endsWith('merkle.json'));
182
- const matching = run(['verify', manifest.output, '--root', moved, '--json'], root);
183
- assert.equal(matching.status, 0, matching.stderr);
184
- assert.equal(JSON.parse(matching.stdout).equal, true);
185
- await write(moved, 'a.txt', 'changed'); await write(moved, 'b.txt', 'new');
186
- const changed = run(['verify', manifest.output, '--root', moved, '--json'], root);
187
- assert.equal(changed.status, 1, changed.stderr);
188
- assert.deepEqual(JSON.parse(changed.stdout).stats, { unchanged: 0, modified: 1, added: 1, removed: 0 });
189
- const diff = run(['diff', root, moved, '--json'], root);
190
- assert.equal(diff.status, 1, diff.stderr);
191
- const invalid = run(['root', '.', '--typo', '--json'], root);
192
- assert.equal(invalid.status, 2); assert.equal(invalid.stdout, '');
193
- assert.match(JSON.parse(invalid.stderr).error, /Unknown option/);
194
- const piped = run(['inspect', '.', '--tui'], root);
195
- assert.equal(piped.status, 0, piped.stderr);
196
- assert.ok(!piped.stdout.includes('\x1b'));
197
- });
198
-
199
169
  test('semantic index records execution domains, environment access, and resolved local imports as Merkle-bound evidence', async (t) => {
200
170
  const root = await fixture(t);
201
171
  await write(root, 'package.json', JSON.stringify({ name: 'boundary-fixture', version: '1.0.0' }));
@@ -1,6 +1,7 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import {
4
+ MERKLE_PERSISTENCE_SCHEMA_VERSION,
4
5
  MERKLE_SNAPSHOT_SCHEMA_SQL,
5
6
  MERKLE_SNAPSHOT_STORAGE_PREFIX,
6
7
  MERKLE_SNAPSHOT_TABLE,
@@ -9,15 +10,19 @@ import {
9
10
  merklePersistenceUpsertSql,
10
11
  merkleSnapshotStorageKey,
11
12
  resolveWranglerMerklePersistence,
12
- } from '../src/lib/merkle/index.js';
13
+ } from '../src/merkle/index.js';
13
14
  import fs from 'node:fs';
14
15
  import os from 'node:os';
15
16
  import path from 'node:path';
16
17
 
17
18
  test('portable Merkle persistence names one table, prefix, and logical asset role', () => {
19
+ assert.equal(MERKLE_PERSISTENCE_SCHEMA_VERSION, 2);
18
20
  assert.equal(MERKLE_SNAPSHOT_TABLE, 'agentsam_fs_merkle_snapshots');
19
21
  assert.equal(MERKLE_SNAPSHOT_STORAGE_PREFIX, 'agentsam_fs_merkle_snapshots');
20
22
  assert.equal(MERKLE_WEBSITE_ASSETS_BINDING, 'WEBSITE_ASSETS');
23
+ assert.match(MERKLE_SNAPSHOT_SCHEMA_SQL, /account_id TEXT NOT NULL/);
24
+ assert.match(MERKLE_SNAPSHOT_SCHEMA_SQL, /repository_id TEXT NOT NULL/);
25
+ assert.doesNotMatch(MERKLE_SNAPSHOT_SCHEMA_SQL, /owner_user_id|\brepo_id\b/);
21
26
  assert.match(MERKLE_SNAPSHOT_SCHEMA_SQL, /metadata_root TEXT/);
22
27
  assert.match(MERKLE_SNAPSHOT_SCHEMA_SQL, /classifier_format TEXT/);
23
28
  assert.match(MERKLE_SNAPSHOT_SCHEMA_SQL, /storage_bucket TEXT/);
@@ -25,12 +30,12 @@ test('portable Merkle persistence names one table, prefix, and logical asset rol
25
30
 
26
31
  test('snapshot storage keys stay beneath the canonical prefix and encode repo identity', () => {
27
32
  const key = merkleSnapshotStorageKey({
28
- ownerUserId: 'au_example',
29
- repoId: 'github:owner/repo',
33
+ accountId: 'au_example',
34
+ repositoryId: 'github:owner/repo',
30
35
  snapshotId: 'mrs_example',
31
36
  });
32
37
  assert.equal(key, 'agentsam_fs_merkle_snapshots/au_example/github%3Aowner%2Frepo/mrs_example.json');
33
- const escaped = merkleSnapshotStorageKey({ ownerUserId: '../oops', repoId: 'repo', snapshotId: 'snap' });
38
+ const escaped = merkleSnapshotStorageKey({ accountId: '../oops', repositoryId: 'repo', snapshotId: 'snap' });
34
39
  assert.ok(!escaped.includes('/../'));
35
40
  assert.ok(escaped.includes('..%2Foops'));
36
41
  });
@@ -63,7 +68,7 @@ test('persistence plan keeps content, policy, and metadata identities separate',
63
68
  },
64
69
  };
65
70
  const plan = buildMerklePersistencePlan({
66
- snapshot, root: process.cwd(), ownerUserId: 'au_test', repoId: 'github:owner/repo', source: 'github',
71
+ snapshot, root: process.cwd(), accountId: 'au_test', repositoryId: 'github:owner/repo', source: 'github',
67
72
  captureKind: 'agent', connectionId: 'conn_test',
68
73
  wrangler: { storage_bucket: 'customer-assets', r2_binding: 'WEBSITE_ASSETS', database_name: 'customer-db', d1_binding: 'DB' },
69
74
  });
@@ -85,7 +90,7 @@ test('non-deploy persistence requires execution provenance', () => {
85
90
  policyHash: `sha256:${'2'.repeat(64)}`, entries: [], stats: { files: 0, directories: 0, symlinks: 0, bytes: 0 },
86
91
  };
87
92
  assert.throws(() => buildMerklePersistencePlan({
88
- snapshot, root: process.cwd(), ownerUserId: 'au_test', repoId: 'github:owner/repo', captureKind: 'agent',
93
+ snapshot, root: process.cwd(), accountId: 'au_test', repositoryId: 'github:owner/repo', captureKind: 'agent',
89
94
  wrangler: { storage_bucket: 'customer-assets', r2_binding: 'WEBSITE_ASSETS', database_name: 'customer-db', d1_binding: 'DB' },
90
95
  }), /execution_provenance_required/);
91
96
  });
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@inneranimalmedia/agentsam-sdk-identity",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "private": true,
5
5
  "type": "module",
6
- "description": "Identity module for @inneranimalmedia/agentsam-sdk (workspace — publish via root SDK)",
6
+ "description": "Identity module for @inneranimalmedia/agentsam-sdk (workspace \u2014 publish via root SDK)",
7
7
  "agentsam": {
8
8
  "system": "identity",
9
9
  "kind": "library",