@inneranimalmedia/agentsam-sdk 2.6.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.
- package/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
- package/docs/RELEASES.md +7 -7
- package/docs/SOURCE_ARCHITECTURE.md +58 -0
- package/docs/TEST_TIERS.md +26 -0
- package/migrations/runtime/0001_cli_runtime.sql +298 -0
- package/package.json +28 -7
- package/packages/agentsam-repository/README.md +15 -0
- package/packages/agentsam-repository/package.json +25 -0
- package/packages/agentsam-repository/src/contracts.js +113 -0
- package/packages/agentsam-repository/src/index.js +3 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
- package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
- package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
- package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
- package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
- package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
- package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
- package/packages/identity/package.json +1 -1
- package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
- package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
- package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
- package/protocol/repository/repository-contract.schema.json +24 -0
- package/protocol/repository/repository-dependency.schema.json +24 -0
- package/protocol/repository/repository-identity.schema.json +17 -0
- package/protocol/rpc/v1/common.proto +16 -0
- package/protocol/rpc/v1/errors.proto +35 -0
- package/protocol/rpc/v1/knowledge.proto +77 -0
- package/services/knowledge/package-lock.json +333 -0
- package/services/knowledge/package.json +5 -1
- package/src/agent/responses-runner.js +63 -35
- package/src/capabilities/repository-snapshot.js +3 -3
- package/src/cli.js +23 -6
- package/src/commands/context-economics.js +17 -2
- package/src/commands/context.js +1 -1
- package/src/commands/db.js +20 -3
- package/src/commands/env.js +90 -0
- package/src/commands/knowledge.js +12 -4
- package/src/commands/merkle-persist.js +30 -11
- package/src/commands/merkle.js +1 -1
- package/src/commands/models.js +123 -65
- package/src/commands/ollama.js +26 -0
- package/src/commands/preferences.js +53 -26
- package/src/commands/shell.js +236 -48
- package/src/errors/contract.js +236 -0
- package/src/errors/index.js +14 -0
- package/src/index.js +13 -1
- package/src/knowledge/service/auth.js +13 -0
- package/src/knowledge/service/grpc-client.js +115 -0
- package/src/knowledge/service/grpc-codec.js +237 -0
- package/src/knowledge/service/grpc-server.js +83 -0
- package/src/knowledge/service/job-engine.js +248 -0
- package/src/knowledge/service/server.js +87 -135
- package/src/knowledge/source.js +1 -1
- package/src/lib/cli-preferences.js +31 -4
- package/src/lib/deploy-receipt/index.js +2 -2
- package/src/lib/knowledge-docker.js +6 -3
- package/src/lib/local-sessions.js +23 -2
- package/src/lib/local-status.js +1 -1
- package/src/lib/project-config.js +1 -1
- package/src/lib/provider-credentials.js +105 -5
- package/src/lib/slash-commands.js +4 -3
- package/src/local/migrations.js +93 -0
- package/src/local/runtime-store.js +141 -0
- package/src/local/sqlite.js +2 -0
- package/src/local-pty/server.js +113 -51
- package/src/models/discovery.js +292 -0
- package/src/providers/anthropic-messages.js +192 -0
- package/src/providers/cloudflare-chat.js +183 -0
- package/src/providers/factory.js +69 -0
- package/src/providers/gemini-generate-content.js +208 -0
- package/src/providers/index.js +5 -0
- package/src/providers/ollama-chat.js +148 -0
- package/src/providers/openai-responses.js +226 -75
- package/src/repository/index.js +14 -2
- package/src/rpc/generated/common_grpc_pb.js +1 -0
- package/src/rpc/generated/common_pb.js +536 -0
- package/src/rpc/generated/errors_grpc_pb.js +1 -0
- package/src/rpc/generated/errors_pb.js +482 -0
- package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
- package/src/rpc/generated/knowledge_pb.js +2168 -0
- package/src/rpc/generated/package.json +3 -0
- package/src/security/trust-boundary.js +2 -2
- package/src/telemetry/events.js +4 -1
- package/src/ui/cli/activity.js +76 -0
- package/src/ui/cli/compaction.js +15 -0
- package/src/ui/cli/footer.js +39 -0
- package/src/ui/cli/help.js +192 -0
- package/src/ui/cli/plan.js +20 -0
- package/src/ui/cli/runtime-events.js +110 -0
- package/src/ui/cli/waiting.js +16 -0
- package/src/ui/merkle/render.js +1 -1
- package/test/cli/preferences-runtime.test.mjs +11 -0
- package/test/cli/runtime-ui.test.mjs +74 -0
- package/test/error-diagnostics.test.mjs +57 -1
- package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
- package/test/integration/cli-help.test.mjs +37 -0
- package/test/integration/knowledge-rpc.test.mjs +112 -0
- package/test/integration/merkle-cli.test.mjs +61 -0
- package/test/integration/merkle-persistence-identity.test.mjs +48 -0
- package/test/integration/provider-env-cli.test.mjs +49 -0
- package/test/integration/provider-factory.test.mjs +197 -0
- package/test/integration/repository-company-graph.test.mjs +90 -0
- package/test/integration/runtime-migrations.test.mjs +82 -0
- package/test/knowledge-service.test.mjs +5 -0
- package/test/knowledge.test.mjs +16 -0
- package/test/live/terminal-transport.live.test.mjs +24 -0
- package/test/local-sessions.test.mjs +7 -1
- package/test/models.test.mjs +101 -4
- package/test/ollama.test.mjs +21 -0
- package/test/portable-context.test.mjs +1 -1
- package/test/provider-credentials.test.mjs +45 -1
- package/test/release-hygiene.test.mjs +13 -5
- package/test/responses-runner.test.mjs +3 -1
- package/test/shell.test.mjs +50 -8
- package/test/terminal/local-pty.mock.test.mjs +151 -0
- /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
|
@@ -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
|
+
}
|
|
@@ -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,
|
|
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([
|
|
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
|
-
|
|
171
|
-
|
|
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
|
|
184
|
-
|
|
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
|
|
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({
|
|
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
|
-
|
|
200
|
-
|
|
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,
|
|
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.
|
|
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
|
|
31
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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 {
|
|
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/
|
|
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
|
-
|
|
29
|
-
|
|
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({
|
|
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(),
|
|
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(),
|
|
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
|
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# AgentSam Company Repository Graph v1
|
|
2
|
+
|
|
3
|
+
This protocol defines the portable vocabulary for company-wide repository identity, public contracts, dependency edges, and Merkle evidence. It does not make the SDK a global database.
|
|
4
|
+
|
|
5
|
+
## Authority split
|
|
6
|
+
|
|
7
|
+
- `agentsam-sdk` defines schemas, enums, builders, Merkle formats, and receipt semantics.
|
|
8
|
+
- The authenticated host owns `account_id` and persists the operational graph.
|
|
9
|
+
- `code_repositories` is the canonical repository registry.
|
|
10
|
+
- `agentsam_repository_contracts` records what a repository provides.
|
|
11
|
+
- `agentsam_repository_dependencies` records what a repository consumes and the policy attached to that edge.
|
|
12
|
+
- `agentsam_fs_merkle_snapshots` indexes exact repository states; full manifests may live in object storage.
|
|
13
|
+
- Deployment and deployment-health tables record whether a source state reached production and whether it remains healthy.
|
|
14
|
+
- MCP may expose graph queries; ExecOS may emit execution evidence. Neither becomes the ownership authority.
|
|
15
|
+
|
|
16
|
+
## Ownership law
|
|
17
|
+
|
|
18
|
+
Persisted company graph records use `account_id` as ownership SSOT. `tenant_id`, `workspace_id`, `user_id`, and `owner_user_id` are not part of this protocol.
|
|
19
|
+
|
|
20
|
+
Portable repository identity is deliberately different: a checked-in repository identity must not embed an account identifier. The host binds its stable `repository_id` to an authenticated `account_id` in the registry.
|
|
21
|
+
|
|
22
|
+
## Canonical repository identity
|
|
23
|
+
|
|
24
|
+
A Git remote is evidence, not authority. The SDK must not derive the canonical `repository_id` from `owner/repo`, a checkout path, a workspace, or a provider URL. Persistence callers provide the `repository_id` from the host registry.
|
|
25
|
+
|
|
26
|
+
## Graph levels
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
code_repositories
|
|
30
|
+
repository identity / company role
|
|
31
|
+
|
|
|
32
|
+
+-- agentsam_repository_contracts
|
|
33
|
+
| what this repository provides
|
|
34
|
+
|
|
|
35
|
+
+-- agentsam_repository_dependencies
|
|
36
|
+
what this repository consumes
|
|
37
|
+
|
|
|
38
|
+
+-- Merkle / AST / Git evidence
|
|
39
|
+
|
|
|
40
|
+
+-- deployments / deployment health
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`codebase_dep_edges` remains an intra-repository code graph. `agentsam_repository_dependencies` is a cross-repository company graph. They are not interchangeable.
|
|
44
|
+
|
|
45
|
+
## Contract records
|
|
46
|
+
|
|
47
|
+
Contract types are:
|
|
48
|
+
|
|
49
|
+
`api | schema | runtime | cli | event | receipt | package`
|
|
50
|
+
|
|
51
|
+
Statuses are:
|
|
52
|
+
|
|
53
|
+
`active | deprecated | retired`
|
|
54
|
+
|
|
55
|
+
A contract record carries a stable `contract_key`, explicit version, manifest path when one exists, and `contract_hash`. The hash is the semantic identity consumers can pin independently of a Git branch name.
|
|
56
|
+
|
|
57
|
+
## Dependency records
|
|
58
|
+
|
|
59
|
+
Dependency types are:
|
|
60
|
+
|
|
61
|
+
`runtime | build | contract | package | deploy | tool | data`
|
|
62
|
+
|
|
63
|
+
Criticality is:
|
|
64
|
+
|
|
65
|
+
`informational | compatible | strict | critical`
|
|
66
|
+
|
|
67
|
+
Failure policy is:
|
|
68
|
+
|
|
69
|
+
`warn | block_certification | block_deploy | degrade`
|
|
70
|
+
|
|
71
|
+
The dependency graph supplies the meaning a Merkle tree cannot infer by itself: who consumes a changed contract and what should happen if the requirement is violated.
|
|
72
|
+
|
|
73
|
+
## Merkle persistence v2
|
|
74
|
+
|
|
75
|
+
Company persistence uses:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
account_id + repository_id + snapshot_id
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Object keys are:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
agentsam_fs_merkle_snapshots/<account_id>/<repository_id>/<snapshot_id>.json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The content root, metadata root, and policy hash remain separate authorities. A future contract/dependency aggregate root may be layered above these records without changing the content Merkle format.
|
|
88
|
+
|
|
89
|
+
## Certification
|
|
90
|
+
|
|
91
|
+
Version 1 intentionally does not require a separate certifications table. A host can derive an initial certification decision from repository identity, contract hashes, dependency requirements, Merkle roots, verification results, deployments, and deployment health. A persistent certification ledger can be added when there is a real need to preserve compatibility statements across an exact set of repository roots.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# AgentSam Merkle Persistence v1
|
|
2
2
|
|
|
3
|
+
> Superseded for current company persistence by `MERKLE_PERSISTENCE_V2.md`. V1 is retained only as the historical contract shipped before the account/repository hard cut.
|
|
4
|
+
|
|
3
5
|
Persistence is a host concern layered on top of the portable Merkle protocol. The SDK exposes the canonical table name, storage prefix, logical asset binding, storage-key helper, and portable SQL schema without owning a customer's database or cloud credentials.
|
|
4
6
|
|
|
5
7
|
## Authority split
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# AgentSam Merkle Persistence v2
|
|
2
|
+
|
|
3
|
+
Persistence is a host concern layered on top of the portable Merkle protocol. Version 2 hard-cuts persisted ownership to `account_id + repository_id` and removes user/workspace/tenant ownership aliases.
|
|
4
|
+
|
|
5
|
+
## Authority split
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
root_hash = filesystem/content authority
|
|
9
|
+
metadata_root = semantic/index authority
|
|
10
|
+
policy_hash = capture-scope authority
|
|
11
|
+
account_id = authenticated ownership authority
|
|
12
|
+
repository_id = canonical repository-registry identity
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
A checkout path, workspace, or user identity must never become `repository_id`. For registered Git repositories, the SDK may derive the canonical provider identity directly from the Git remote (for example `github:owner/repo`), matching the `code_repositories.id` convention; local repositories may fall back to the committed project manifest.
|
|
16
|
+
|
|
17
|
+
The optional searchable index table remains `agentsam_fs_merkle_snapshots`. Full manifests may be stored under the provider-neutral object prefix `agentsam_fs_merkle_snapshots/`.
|
|
18
|
+
|
|
19
|
+
Typical keys are:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
agentsam_fs_merkle_snapshots/<account_id>/<repository_id>/<snapshot_id>.json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The hosting application chooses physical storage and database bindings. The SDK never owns cloud credentials and never invents account or repository authority.
|
|
26
|
+
|
|
27
|
+
## CLI publication
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
agentsam login
|
|
31
|
+
agentsam merkle snapshot . --semantic --out .agentsam/merkle.json
|
|
32
|
+
agentsam merkle persist .agentsam/merkle.json \
|
|
33
|
+
--wrangler-config path/to/wrangler.toml \
|
|
34
|
+
--capture-kind deploy \
|
|
35
|
+
--deployment-id "$DEPLOYMENT_ID"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The CLI reads `account_id` from the authenticated local AgentSam session and resolves `repository_id` from Git/provider identity, with the committed project manifest as fallback for local repositories. Programmatic hosts still pass authenticated `account_id + repository_id` directly to `buildMerklePersistencePlan`. For non-deploy captures, execution provenance remains required through a connection or runtime lease. `--dry-run` emits the exact storage/index plan without writes; `--r2-only` uploads the manifest without a D1 index write.
|
|
39
|
+
|
|
40
|
+
Version 1 used `owner_user_id` / `repo_id` and is superseded for current company persistence.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.inneranimalmedia.com/agentsam/repository/repository-contract.schema.json",
|
|
4
|
+
"title": "AgentSam Repository Contract Record",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_version", "id", "account_id", "repository_id", "contract_key", "contract_version", "contract_type", "name", "contract_hash", "status"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": 1 },
|
|
10
|
+
"id": { "type": "string", "minLength": 1 },
|
|
11
|
+
"account_id": { "type": "string", "minLength": 1 },
|
|
12
|
+
"repository_id": { "type": "string", "minLength": 1 },
|
|
13
|
+
"contract_key": { "type": "string", "minLength": 1 },
|
|
14
|
+
"contract_version": { "type": "string", "minLength": 1 },
|
|
15
|
+
"contract_type": { "enum": ["api", "schema", "runtime", "cli", "event", "receipt", "package"] },
|
|
16
|
+
"name": { "type": "string", "minLength": 1 },
|
|
17
|
+
"description": { "type": ["string", "null"] },
|
|
18
|
+
"manifest_path": { "type": ["string", "null"] },
|
|
19
|
+
"contract_hash": { "type": "string", "minLength": 1 },
|
|
20
|
+
"status": { "enum": ["active", "deprecated", "retired"] },
|
|
21
|
+
"created_at": { "type": ["integer", "null"], "minimum": 0 },
|
|
22
|
+
"updated_at": { "type": ["integer", "null"], "minimum": 0 }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.inneranimalmedia.com/agentsam/repository/repository-dependency.schema.json",
|
|
4
|
+
"title": "AgentSam Repository Dependency Record",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_version", "id", "account_id", "source_repository_id", "target_repository_id", "dependency_type", "criticality", "failure_policy"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": 1 },
|
|
10
|
+
"id": { "type": "string", "minLength": 1 },
|
|
11
|
+
"account_id": { "type": "string", "minLength": 1 },
|
|
12
|
+
"source_repository_id": { "type": "string", "minLength": 1 },
|
|
13
|
+
"target_repository_id": { "type": "string", "minLength": 1 },
|
|
14
|
+
"dependency_type": { "enum": ["runtime", "build", "contract", "package", "deploy", "tool", "data"] },
|
|
15
|
+
"contract_id": { "type": ["string", "null"] },
|
|
16
|
+
"criticality": { "enum": ["informational", "compatible", "strict", "critical"] },
|
|
17
|
+
"required_version": { "type": ["string", "null"] },
|
|
18
|
+
"required_contract_hash": { "type": ["string", "null"] },
|
|
19
|
+
"failure_policy": { "enum": ["warn", "block_certification", "block_deploy", "degrade"] },
|
|
20
|
+
"description": { "type": ["string", "null"] },
|
|
21
|
+
"created_at": { "type": ["integer", "null"], "minimum": 0 },
|
|
22
|
+
"updated_at": { "type": ["integer", "null"], "minimum": 0 }
|
|
23
|
+
}
|
|
24
|
+
}
|