@inneranimalmedia/agentsam-sdk 2.6.3 → 2.6.4
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/AGENTSAM.md +6 -0
- package/README.md +16 -1
- package/bin/agentsam +16 -1
- package/docs/BRAND_INTELLIGENCE.md +1 -1
- package/docs/architecture/AGENTSAM_DISTRIBUTION_OWNERSHIP.md +176 -0
- package/docs/architecture/AGENTSAM_GO_RUNTIME.md +282 -0
- package/docs/architecture/CODEBASEINDEX_GUIDED_PATH.md +202 -0
- package/docs/architecture/FS_E2E_CLOSURE_RECEIPT.md +59 -0
- package/docs/architecture/LOCAL_FS_AUTHORITY.md +38 -0
- package/docs/architecture/LOCAL_STUDIO_CLOUDFLARE_OAUTH.md +28 -0
- package/docs/architecture/PLAN_CLI_AND_LOCAL_STUDIO_DESKTOP.md +96 -0
- package/docs/architecture/SAM_ACTIVITY_RECOVERY_RECEIPT.md +39 -0
- package/docs/architecture/SAM_DECISION_WORK_RECEIPT.md +54 -0
- package/docs/architecture/SAM_KERNEL.md +181 -0
- package/docs/architecture/SAM_MACHINE_NORMALIZATION_PRECOMMIT_REPORT.md +208 -0
- package/docs/architecture/SLASH_SKILLS_PORTABLE.md +116 -0
- package/docs/architecture/fs-e2e-receipt.latest.json +42 -0
- package/docs/architecture/previews/codebaseindex-guided-path/CODEBASEINDEX_GUIDED_PATH.md +202 -0
- package/docs/architecture/previews/codebaseindex-guided-path/index.html +321 -0
- package/migrations/d1/0010_portable_tickets_memory.sql +140 -0
- package/migrations/d1/0011_agentsam_skill_v2.sql +185 -0
- package/migrations/d1/0011b_agentsam_skill_v2_cutover.sql +22 -0
- package/migrations/d1/0011c_agentsam_skill_v2_backfill.sql +76 -0
- package/migrations/d1/0011d_agentsam_skill_v2_retrieval_revisions.sql +50 -0
- package/migrations/d1/0012_agentsam_tools_required_seed.sql +67 -0
- package/migrations/d1/0013_identity_oauth_states.sql +15 -0
- package/migrations/d1/0014_auth_event_log.sql +20 -0
- package/migrations/d1/0015_identity_oauth_state_app_id.sql +3 -0
- package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +16 -0
- package/migrations/sqlite/agentsam_skill_retrieval.portable.sql +42 -0
- package/package.json +19 -4
- package/packages/agentsam-contracts/src/errors.ts +16 -0
- package/packages/agentsam-errors/src/envelope.js +53 -0
- package/packages/agentsam-errors/src/index.js +1 -0
- package/packages/agentsam-errors/src/recovery.js +301 -0
- package/packages/agentsam-knowledge/src/providers/index.js +18 -6
- package/packages/connectors/cloudflare/src/routes.js +9 -0
- package/packages/connectors/cloudflare/tests/connector.test.mjs +26 -1
- package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
- package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +11 -2
- package/packages/identity/docs/PORTABLE_IDENTITY_ARCHITECTURE.md +50 -0
- package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +31 -0
- package/packages/identity/migrations/sqlite/001_identity_core.sql +99 -0
- package/packages/identity/migrations/sqlite/002_identity_oauth_client.sql +38 -0
- package/packages/identity/migrations/sqlite/003_identity_oauth_server.sql +64 -0
- package/packages/identity/package.json +2 -2
- package/packages/identity/src/adapters/cloudflare-d1/index.js +122 -22
- package/packages/identity/src/adapters/sqlite/index.js +319 -0
- package/packages/identity/src/app/verify-app.js +95 -0
- package/packages/identity/src/contracts/identity-store.js +115 -0
- package/packages/identity/src/contracts/route-ids.js +30 -0
- package/packages/identity/src/contracts/route-projection.js +218 -0
- package/packages/identity/src/contracts/routes.js +11 -0
- package/packages/identity/src/core/browser-paths.js +4 -5
- package/packages/identity/src/core/constants.js +13 -8
- package/packages/identity/src/core/session-policy.js +32 -0
- package/packages/identity/src/frontend/auth-portal/pages/login.html +10 -10
- package/packages/identity/src/frontend/auth-portal/pages/reset.html +3 -3
- package/packages/identity/src/frontend/auth-portal/pages/signup.html +3 -3
- package/packages/identity/src/frontend/auth-portal/preview/dashboard-stub.html +1 -1
- package/packages/identity/src/index.js +25 -0
- package/packages/identity/src/oauth/README.md +10 -4
- package/packages/identity/src/oauth/credentials.js +20 -13
- package/packages/identity/src/oauth/finalize-inbound.js +1 -1
- package/packages/identity/src/oauth/iam-platform.js +8 -7
- package/packages/identity/src/oauth/redirect-paths.js +27 -36
- package/packages/identity/src/server/identity-service.js +30 -13
- package/packages/identity/src/server/mount-policy.js +30 -0
- package/packages/identity/src/server/post-auth.js +79 -0
- package/packages/identity/src/server/worker-router.js +104 -72
- package/packages/identity/tests/finalize-inbound-oauth.test.mjs +6 -6
- package/packages/identity/tests/iam-provider.test.mjs +1 -1
- package/packages/identity/tests/identity-service.test.mjs +36 -5
- package/packages/identity/tests/oauth-credentials.test.mjs +3 -1
- package/packages/identity/tests/portable-identity-architecture.test.mjs +157 -0
- package/packages/identity/tests/session-routes-policy.test.mjs +21 -0
- package/packages/theme-church-site/package.json +2 -1
- package/packages/theme-church-site/src/index.js +1 -0
- package/packages/theme-companions-site/package.json +2 -1
- package/packages/theme-companions-site/src/index.js +1 -0
- package/packages/theme-floors-site/package.json +2 -1
- package/packages/theme-floors-site/src/index.js +1 -0
- package/packages/theme-fuelnfree-site/package.json +2 -1
- package/packages/theme-fuelnfree-site/src/index.js +1 -0
- package/packages/theme-handyman-site/package.json +2 -1
- package/packages/theme-handyman-site/src/index.js +1 -0
- package/packages/theme-insurance-site/package.json +2 -1
- package/packages/theme-insurance-site/src/index.js +1 -0
- package/packages/theme-shinshu-site/package.json +2 -1
- package/packages/theme-shinshu-site/src/index.js +1 -0
- package/protocol/apps/agentsam.app.v1.schema.json +51 -0
- package/protocol/brand/brandpack.v1.schema.json +43 -0
- package/protocol/credentials/issue.v1.schema.json +38 -0
- package/protocol/database/connection.v1.schema.json +41 -0
- package/protocol/embeddings/embedding-profile.v1.schema.json +20 -0
- package/protocol/errors/error-envelope.schema.json +135 -1
- package/protocol/errors/recovery.v1.schema.json +50 -0
- package/protocol/runtime/workspace-fs.v1.schema.json +71 -0
- package/protocol/sam/activity.v1.schema.json +48 -0
- package/protocol/sam/answer.v1.schema.json +35 -0
- package/protocol/sam/calibration.v1.schema.json +21 -0
- package/protocol/sam/decision-receipt.v1.schema.json +29 -0
- package/protocol/sam/evaluation.v1.schema.json +19 -0
- package/protocol/sam/operation.schema.json +66 -0
- package/protocol/sam/outcome.v1.schema.json +36 -0
- package/protocol/sam/question.v1.schema.json +26 -0
- package/protocol/sam/registry.seed.json +153 -0
- package/protocol/sam/result.schema.json +53 -0
- package/protocol/sam/state.v1.schema.json +23 -0
- package/protocol/skills/agentsam.interaction.v1.schema.json +50 -0
- package/protocol/skills/agentsam.skill.v1.schema.json +57 -0
- package/protocol/ui/icon-registry.mjs +226 -0
- package/protocol/ui/icon.v1.schema.json +51 -0
- package/skills/README.md +22 -9
- package/skills/agentsam-codebaseindex/SKILL.md +225 -0
- package/skills/catalog.json +14 -0
- package/src/cli/command-catalog.js +130 -0
- package/src/cli/dispatch.js +48 -0
- package/src/cli.js +43 -1
- package/src/commands/api-key.js +244 -0
- package/src/commands/app.js +60 -28
- package/src/commands/brand.js +17 -19
- package/src/commands/codebaseindex.js +688 -0
- package/src/commands/env.js +152 -25
- package/src/commands/go.js +366 -53
- package/src/commands/interaction-clack.js +115 -0
- package/src/commands/models.js +1 -1
- package/src/commands/providers.js +62 -14
- package/src/commands/shell.js +43 -2
- package/src/commands/skill.js +248 -0
- package/src/commands/skills.js +1 -1
- package/src/commands/start-local.js +4 -0
- package/src/commands/whoami.js +90 -18
- package/src/go/build.js +229 -39
- package/src/go/cloudflare.js +506 -105
- package/src/go/container.js +120 -0
- package/src/go/contract.js +9 -4
- package/src/go/discover.js +149 -33
- package/src/go/index.js +15 -3
- package/src/go/native-probe-runner.mjs +119 -0
- package/src/go/{registry.js → official-registry.js} +64 -7
- package/src/go/official-release.js +10 -0
- package/src/go/receipts.js +77 -10
- package/src/go/verify.js +9 -2
- package/src/index.js +27 -0
- package/src/indexing/ingest/discover-models.js +298 -0
- package/src/indexing/ingest/inventory.js +243 -0
- package/src/indexing/ingest/job-graph.js +181 -0
- package/src/indexing/ingest/materials.js +210 -0
- package/src/lib/provider-credentials.js +63 -21
- package/src/lib/slash-commands.js +1 -0
- package/src/local-fs/capability.js +121 -0
- package/src/local-fs/freshness.js +75 -0
- package/src/local-fs/index.js +385 -0
- package/src/local-fs/paths.js +100 -0
- package/src/local-pty/server.js +295 -19
- package/src/mcp/client.js +2 -2
- package/src/models/ai-access-onboarding.js +112 -0
- package/src/models/discovery.js +10 -2
- package/src/models/inventory-core.js +9 -1
- package/src/sam/activity/index.js +183 -0
- package/src/sam/client.js +252 -0
- package/src/sam/decision/calibration.js +109 -0
- package/src/sam/decision/confidence.js +126 -0
- package/src/sam/decision/evaluate.js +157 -0
- package/src/sam/decision/evaluators/deterministic.js +341 -0
- package/src/sam/decision/evaluators/heuristic.js +61 -0
- package/src/sam/decision/evaluators/select.js +50 -0
- package/src/sam/decision/evaluators/semantic.js +149 -0
- package/src/sam/decision/hierarchical.js +61 -0
- package/src/sam/decision/index.js +53 -0
- package/src/sam/decision/policy.js +86 -0
- package/src/sam/decision/questions.js +120 -0
- package/src/sam/decision/receipt.js +148 -0
- package/src/sam/decision/state.js +117 -0
- package/src/sam/decision/types.js +22 -0
- package/src/sam/decision/validate.js +200 -0
- package/src/sam/define.js +51 -0
- package/src/sam/index.js +65 -0
- package/src/sam/operations/brand-scan.js +62 -0
- package/src/sam/operations/cad-blender-inspect.js +36 -0
- package/src/sam/operations/codebaseindex-ingest.js +49 -0
- package/src/sam/operations/decision-evaluate.js +59 -0
- package/src/sam/operations/planning-astar.js +77 -0
- package/src/sam/operations/planning-goap.js +60 -0
- package/src/sam/operations/repository-inspect.js +72 -0
- package/src/sam/operations/security-scan.js +33 -0
- package/src/sam/operations/terminal-exec.js +29 -0
- package/src/sam/planning/astar.js +311 -0
- package/src/sam/planning/goap.js +177 -0
- package/src/sam/planning/index.js +21 -0
- package/src/sam/planning/state.js +84 -0
- package/src/sam/registry.js +48 -0
- package/src/sam/result.js +77 -0
- package/src/sam/seed.js +44 -0
- package/src/sam/types.js +91 -0
- package/src/skills/catalog.js +64 -0
- package/src/skills/content-resolver.js +124 -0
- package/src/skills/hosted-store.js +37 -0
- package/src/skills/index.js +29 -64
- package/src/skills/interaction.js +102 -0
- package/src/skills/local-store.js +228 -0
- package/src/skills/manifest.js +104 -0
- package/src/skills/metrics.js +31 -0
- package/src/skills/registry.js +184 -0
- package/src/skills/runtime.js +287 -0
- package/src/skills/slash.js +44 -0
- package/src/ui/cli/help.js +94 -101
- package/test/cli/api-key-env-whoami.test.mjs +129 -0
- package/test/cli/codebaseindex-plan-ux.test.mjs +37 -0
- package/test/cli/go.test.mjs +62 -5
- package/test/cli/skill-npm-and-env.test.mjs +52 -0
- package/test/cli/wireframes-go-registry.test.mjs +87 -2
- package/test/go/build-source-identity.test.mjs +31 -0
- package/test/go/cloudflare-probe.test.mjs +274 -10
- package/test/go/distribution.test.mjs +28 -0
- package/test/integration/ai-access-onboarding.test.mjs +47 -0
- package/test/integration/cli-help.test.mjs +1 -1
- package/test/integration/cms-site-tenancy-contract.test.mjs +6 -6
- package/test/integration/codebaseindex-ingest.test.mjs +166 -0
- package/test/integration/icon-registry.test.mjs +67 -0
- package/test/integration/ingest-discover-models.test.mjs +30 -0
- package/test/integration/install-script.test.mjs +12 -9
- package/test/integration/local-fs.test.mjs +113 -0
- package/test/integration/provider-env-cli.test.mjs +2 -1
- package/test/integration/sam-activity-recovery.test.mjs +147 -0
- package/test/integration/sam-decision.test.mjs +584 -0
- package/test/integration/sam-kernel.test.mjs +99 -0
- package/test/integration/sam-planning-astar.test.mjs +279 -0
- package/test/integration/skill-runtime.test.mjs +240 -0
- package/test/integration/studio-fs-pty-e2e.test.mjs +294 -0
- package/test/models.test.mjs +14 -7
- package/test/shell.test.mjs +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import test from 'node:test';
|
|
3
3
|
import { renderArchitectureExplorer, renderOperationInspector, drawBox } from '../../src/ui/wireframes.js';
|
|
4
|
-
import { buildGoProductRegistrySql } from '../../src/go/registry.js';
|
|
4
|
+
import { applyInnerAnimalMediaOfficialGoProductRegistry, buildGoProductRegistrySql } from '../../src/go/official-registry.js';
|
|
5
5
|
|
|
6
6
|
test('architecture explorer is boxed reasoning scenery', () => {
|
|
7
7
|
const view = renderArchitectureExplorer({
|
|
@@ -38,7 +38,7 @@ test('drawBox pads visible ANSI width', () => {
|
|
|
38
38
|
test('buildGoProductRegistrySql upserts product + relationships without inventing tables', () => {
|
|
39
39
|
const sql = buildGoProductRegistrySql({
|
|
40
40
|
product: 'agentsam-go-worker',
|
|
41
|
-
status: '
|
|
41
|
+
status: 'deployed',
|
|
42
42
|
url: 'https://agentsam-go-worker.example.workers.dev',
|
|
43
43
|
commit: 'abc123',
|
|
44
44
|
health: 'healthy',
|
|
@@ -52,3 +52,88 @@ test('buildGoProductRegistrySql upserts product + relationships without inventin
|
|
|
52
52
|
assert.doesNotMatch(sql, /CREATE TABLE/);
|
|
53
53
|
assert.doesNotMatch(sql, /account_id/);
|
|
54
54
|
});
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
test('InnerAnimalMedia remote registry requires the explicit official-release guard', () => {
|
|
58
|
+
const preferred = process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE;
|
|
59
|
+
const legacy = process.env.AGENTSAM_IAM_OFFICIAL_RELEASE;
|
|
60
|
+
|
|
61
|
+
delete process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE;
|
|
62
|
+
delete process.env.AGENTSAM_IAM_OFFICIAL_RELEASE;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
assert.throws(
|
|
66
|
+
() => applyInnerAnimalMediaOfficialGoProductRegistry({
|
|
67
|
+
productRoot: '/tmp/agentsam-test-state',
|
|
68
|
+
officialRelease: true,
|
|
69
|
+
skipRemote: true,
|
|
70
|
+
}),
|
|
71
|
+
/inneranimalmedia_registry_official_release_required/,
|
|
72
|
+
);
|
|
73
|
+
} finally {
|
|
74
|
+
if (preferred == null) {
|
|
75
|
+
delete process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE;
|
|
76
|
+
} else {
|
|
77
|
+
process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE = preferred;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (legacy == null) {
|
|
81
|
+
delete process.env.AGENTSAM_IAM_OFFICIAL_RELEASE;
|
|
82
|
+
} else {
|
|
83
|
+
process.env.AGENTSAM_IAM_OFFICIAL_RELEASE = legacy;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('InnerAnimalMedia official registry normalizes deployed lifecycle status to D1 production', () => {
|
|
89
|
+
const prior = process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE;
|
|
90
|
+
process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE = '1';
|
|
91
|
+
try {
|
|
92
|
+
const result = applyInnerAnimalMediaOfficialGoProductRegistry({
|
|
93
|
+
productRoot: null,
|
|
94
|
+
officialRelease: true,
|
|
95
|
+
status: 'deployed',
|
|
96
|
+
dryRun: true,
|
|
97
|
+
skipRemote: true,
|
|
98
|
+
});
|
|
99
|
+
assert.match(result.sql, /'production'/);
|
|
100
|
+
assert.doesNotMatch(result.sql, /'deployed',\n 'AgentSam Go runtime/);
|
|
101
|
+
} finally {
|
|
102
|
+
if (prior == null) delete process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE;
|
|
103
|
+
else process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE = prior;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
test('legacy AGENTSAM_IAM_OFFICIAL_RELEASE guard remains backward compatible', () => {
|
|
109
|
+
const preferred = process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE;
|
|
110
|
+
const legacy = process.env.AGENTSAM_IAM_OFFICIAL_RELEASE;
|
|
111
|
+
|
|
112
|
+
delete process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE;
|
|
113
|
+
process.env.AGENTSAM_IAM_OFFICIAL_RELEASE = '1';
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const result = applyInnerAnimalMediaOfficialGoProductRegistry({
|
|
117
|
+
productRoot: null,
|
|
118
|
+
officialRelease: true,
|
|
119
|
+
status: 'deployed',
|
|
120
|
+
dryRun: true,
|
|
121
|
+
skipRemote: true,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
assert.equal(result.ok, true);
|
|
125
|
+
assert.match(result.sql, /'production'/);
|
|
126
|
+
} finally {
|
|
127
|
+
if (preferred == null) {
|
|
128
|
+
delete process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE;
|
|
129
|
+
} else {
|
|
130
|
+
process.env.AGENTSAM_INNERANIMALMEDIA_OFFICIAL_RELEASE = preferred;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (legacy == null) {
|
|
134
|
+
delete process.env.AGENTSAM_IAM_OFFICIAL_RELEASE;
|
|
135
|
+
} else {
|
|
136
|
+
process.env.AGENTSAM_IAM_OFFICIAL_RELEASE = legacy;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
repositoryIdFromGitContext,
|
|
6
|
+
} from '../../src/go/build.js';
|
|
7
|
+
|
|
8
|
+
test('Go build source canonicalizes GitHub repository identity', () => {
|
|
9
|
+
assert.equal(
|
|
10
|
+
repositoryIdFromGitContext({
|
|
11
|
+
remoteHost: 'github.com',
|
|
12
|
+
repoFullName: 'SamPrimeaux/agentsam-sdk',
|
|
13
|
+
}),
|
|
14
|
+
'github:samprimeaux/agentsam-sdk',
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('Go build source preserves provider-qualified non-GitHub identity', () => {
|
|
19
|
+
assert.equal(
|
|
20
|
+
repositoryIdFromGitContext({
|
|
21
|
+
remoteHost: 'gitlab.example.com',
|
|
22
|
+
repoFullName: 'Owner/Repo',
|
|
23
|
+
}),
|
|
24
|
+
'gitlab.example.com:Owner/Repo',
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('Go build source returns null when remote identity is unavailable', () => {
|
|
29
|
+
assert.equal(repositoryIdFromGitContext(null), null);
|
|
30
|
+
assert.equal(repositoryIdFromGitContext({}), null);
|
|
31
|
+
});
|
|
@@ -1,53 +1,317 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import test from 'node:test';
|
|
3
|
-
import {
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import {
|
|
6
|
+
deployGoCloudflare,
|
|
7
|
+
extractWorkersDevUrl,
|
|
8
|
+
probeGoDeployment,
|
|
9
|
+
readLatestWranglerDeployment,
|
|
10
|
+
resolveWranglerIdentity,
|
|
11
|
+
resolveConfiguredDeploymentUrl,
|
|
12
|
+
} from '../../src/go/cloudflare.js';
|
|
4
13
|
import { buildProductRow } from '../../src/go/receipts.js';
|
|
5
14
|
|
|
15
|
+
const SDK_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
16
|
+
const PRODUCT_ROOT = path.join(SDK_ROOT, 'apps/agentsam-go-worker');
|
|
17
|
+
const EXPECTED_HASH = '2e60bba13dc2bc37d75dd2ce5deb25466f19cb2994e20889388948879875eae9';
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
test('configured custom domain is canonical Go deployment URL', () => {
|
|
21
|
+
assert.equal(
|
|
22
|
+
resolveConfiguredDeploymentUrl(PRODUCT_ROOT),
|
|
23
|
+
'https://runtime.inneranimalmedia.com',
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
6
27
|
test('extractWorkersDevUrl parses wrangler output', () => {
|
|
7
28
|
const url = extractWorkersDevUrl('Published agentsam-go-worker\n https://agentsam-go-worker.example.workers.dev\n');
|
|
8
29
|
assert.equal(url, 'https://agentsam-go-worker.example.workers.dev');
|
|
9
30
|
});
|
|
10
31
|
|
|
11
|
-
test('probeGoDeployment requires deterministic
|
|
32
|
+
test('probeGoDeployment requires edge, source identity, capabilities, deterministic functions and ErrorEnvelope', async () => {
|
|
12
33
|
const responses = new Map([
|
|
13
|
-
['/
|
|
14
|
-
['/
|
|
15
|
-
['/
|
|
34
|
+
['/', { status: 200, body: { ok: true, edge: 'worker', runtime: 'go/native-container', source: 'git:abc123' } }],
|
|
35
|
+
['/edge/health', { status: 200, body: { ok: true, edge: 'worker', runtime: 'go/native-container', source: 'git:abc123' } }],
|
|
36
|
+
['/health', {
|
|
37
|
+
status: 200,
|
|
38
|
+
body: {
|
|
39
|
+
ok: true,
|
|
40
|
+
service: 'agentsam-go-worker',
|
|
41
|
+
runtime: 'go',
|
|
42
|
+
target: 'cloudflare',
|
|
43
|
+
build: { source: 'git:abc123', commit: 'abc123' },
|
|
44
|
+
},
|
|
45
|
+
}],
|
|
46
|
+
['/v1/runtime', {
|
|
47
|
+
status: 200,
|
|
48
|
+
body: { schema: 'agentsam.go-runtime.v1', os: 'linux', capabilities: ['hash', 'inspect', 'runtime', 'capabilities'] },
|
|
49
|
+
}],
|
|
50
|
+
['/v1/capabilities', {
|
|
51
|
+
status: 200,
|
|
52
|
+
body: { schema: 'agentsam.go-capabilities.v1', capabilities: ['hash', 'inspect', 'runtime', 'capabilities'] },
|
|
53
|
+
}],
|
|
54
|
+
['/v1/hash', { status: 200, body: { hash: EXPECTED_HASH, algorithm: 'sha256' } }],
|
|
16
55
|
['/v1/inspect', { status: 200, body: { findings: [{ kind: 'hardcoded_color', value: '#2563eb' }] } }],
|
|
17
56
|
]);
|
|
18
57
|
let inspectCalls = 0;
|
|
19
|
-
const fetchImpl = async (url
|
|
58
|
+
const fetchImpl = async (url) => {
|
|
20
59
|
const pathname = new URL(url).pathname;
|
|
21
60
|
if (pathname === '/v1/inspect') {
|
|
22
61
|
inspectCalls += 1;
|
|
23
62
|
if (inspectCalls === 2) {
|
|
24
63
|
return {
|
|
25
64
|
status: 400,
|
|
26
|
-
async json() {
|
|
65
|
+
async json() {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
schema_version: 1,
|
|
69
|
+
code: 'INVALID_ARGUMENT',
|
|
70
|
+
reason: 'input_invalid',
|
|
71
|
+
};
|
|
72
|
+
},
|
|
27
73
|
};
|
|
28
74
|
}
|
|
29
75
|
}
|
|
30
76
|
const row = responses.get(pathname);
|
|
77
|
+
assert.ok(row, 'unexpected probe path: ' + pathname);
|
|
31
78
|
return {
|
|
32
79
|
status: row.status,
|
|
33
80
|
async json() { return row.body; },
|
|
34
81
|
};
|
|
35
82
|
};
|
|
36
|
-
|
|
83
|
+
|
|
84
|
+
const result = await probeGoDeployment('https://example.workers.dev', {
|
|
85
|
+
fetchImpl,
|
|
86
|
+
expectedSource: 'git:abc123',
|
|
87
|
+
expectedSourceCommit: 'abc123',
|
|
88
|
+
expectedTarget: 'cloudflare',
|
|
89
|
+
});
|
|
37
90
|
assert.equal(result.ok, true);
|
|
91
|
+
assert.equal(result.checks.edge_root, true);
|
|
92
|
+
assert.equal(result.checks.edge_health, true);
|
|
93
|
+
assert.equal(result.checks.source_identity, true);
|
|
94
|
+
assert.equal(result.checks.source_commit, true);
|
|
95
|
+
assert.equal(result.checks.capabilities, true);
|
|
96
|
+
assert.equal(result.results.deterministic_hash.ok, true);
|
|
38
97
|
assert.equal(result.results.deterministic_inspect.ok, true);
|
|
39
98
|
assert.equal(result.results.malformed_rejected.ok, true);
|
|
40
99
|
});
|
|
41
100
|
|
|
42
|
-
test('
|
|
101
|
+
test('readLatestWranglerDeployment normalizes authoritative deployment/version identity', () => {
|
|
102
|
+
const spawn = () => ({
|
|
103
|
+
status: 0,
|
|
104
|
+
stdout: JSON.stringify([
|
|
105
|
+
{
|
|
106
|
+
id: 'dep_old',
|
|
107
|
+
created_on: '2026-09-24T00:00:00Z',
|
|
108
|
+
versions: [{ version_id: 'ver_old', percentage: 100 }],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: 'dep_new',
|
|
112
|
+
created_on: '2026-09-25T00:00:00Z',
|
|
113
|
+
versions: [{ version_id: 'ver_new', percentage: 100 }],
|
|
114
|
+
},
|
|
115
|
+
]),
|
|
116
|
+
stderr: '',
|
|
117
|
+
});
|
|
118
|
+
const result = readLatestWranglerDeployment(PRODUCT_ROOT, 'agentsam-go-worker', { spawn });
|
|
119
|
+
assert.equal(result.ok, true);
|
|
120
|
+
assert.equal(result.deployment_id, 'dep_new');
|
|
121
|
+
assert.equal(result.version_id, 'ver_new');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('resolveWranglerIdentity fails closed when authentication is unavailable', () => {
|
|
125
|
+
const spawn = () => ({ status: 1, stdout: '', stderr: 'not logged in' });
|
|
126
|
+
const result = resolveWranglerIdentity(PRODUCT_ROOT, { spawn });
|
|
127
|
+
assert.equal(result.ok, false);
|
|
128
|
+
assert.equal(result.authenticated, false);
|
|
129
|
+
assert.equal(result.error, 'cloudflare_not_authenticated');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('resolveWranglerIdentity requires an explicit account when multiple are available', () => {
|
|
133
|
+
const spawn = () => ({
|
|
134
|
+
status: 0,
|
|
135
|
+
stdout: JSON.stringify({
|
|
136
|
+
loggedIn: true,
|
|
137
|
+
authType: 'OAuth Token',
|
|
138
|
+
accounts: [
|
|
139
|
+
{ id: 'acc_a', name: 'Account A', type: 'standard' },
|
|
140
|
+
{ id: 'acc_b', name: 'Account B', type: 'standard' },
|
|
141
|
+
],
|
|
142
|
+
}),
|
|
143
|
+
stderr: '',
|
|
144
|
+
});
|
|
145
|
+
const ambiguous = resolveWranglerIdentity(PRODUCT_ROOT, { spawn });
|
|
146
|
+
assert.equal(ambiguous.ok, false);
|
|
147
|
+
assert.equal(ambiguous.error, 'cloudflare_account_ambiguous');
|
|
148
|
+
|
|
149
|
+
const selected = resolveWranglerIdentity(PRODUCT_ROOT, {
|
|
150
|
+
spawn,
|
|
151
|
+
requestedAccountId: 'acc_b',
|
|
152
|
+
});
|
|
153
|
+
assert.equal(selected.ok, true);
|
|
154
|
+
assert.equal(selected.account.id, 'acc_b');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('Cloudflare self-host dry run binds the resolved account and never mutates InnerAnimalMedia D1', async () => {
|
|
158
|
+
const calls = [];
|
|
159
|
+
const cloudflareIdentity = {
|
|
160
|
+
ok: true,
|
|
161
|
+
authenticated: true,
|
|
162
|
+
auth_type: 'OAuth Token',
|
|
163
|
+
account: { id: 'acc_user', name: 'User Cloudflare', type: 'standard' },
|
|
164
|
+
accounts: [{ id: 'acc_user', name: 'User Cloudflare', type: 'standard' }],
|
|
165
|
+
};
|
|
166
|
+
const spawn = (command, args = [], options = {}) => {
|
|
167
|
+
calls.push({ command, args, env: options.env || {} });
|
|
168
|
+
if (command === 'docker' && args[0] === 'info') {
|
|
169
|
+
return { status: 0, stdout: 'ok', stderr: '' };
|
|
170
|
+
}
|
|
171
|
+
if (args.includes('deploy') && args.includes('--dry-run')) {
|
|
172
|
+
assert.equal(options.env.CLOUDFLARE_ACCOUNT_ID, 'acc_user');
|
|
173
|
+
assert.ok(args.includes('AGENTSAM_BUILD_SOURCE:npm:@inneranimalmedia/agentsam-go-worker@0.1.0'));
|
|
174
|
+
return { status: 0, stdout: 'Total Upload: 1 KiB', stderr: '' };
|
|
175
|
+
}
|
|
176
|
+
throw new Error('unexpected spawn: ' + [command, ...args].join(' '));
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const result = await deployGoCloudflare({
|
|
180
|
+
productRoot: PRODUCT_ROOT,
|
|
181
|
+
product: 'agentsam-go-worker',
|
|
182
|
+
dryRun: true,
|
|
183
|
+
accountId: 'acc_user',
|
|
184
|
+
cloudflareIdentity,
|
|
185
|
+
source: {
|
|
186
|
+
identity: 'npm:@inneranimalmedia/agentsam-go-worker@0.1.0',
|
|
187
|
+
commit: null,
|
|
188
|
+
package_name: '@inneranimalmedia/agentsam-go-worker',
|
|
189
|
+
package_version: '0.1.0',
|
|
190
|
+
},
|
|
191
|
+
builtAt: '2026-09-25T00:00:00.000Z',
|
|
192
|
+
spawn,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
assert.equal(result.deployed, false);
|
|
196
|
+
assert.equal(result.dryRunValidated, true);
|
|
197
|
+
assert.equal(result.receipt.dry_run, true);
|
|
198
|
+
assert.equal(result.receipt.skipped_deploy, false);
|
|
199
|
+
assert.equal(result.receipt.registry_mode, 'self_host_local');
|
|
200
|
+
assert.equal(result.receipt.cloudflare.account_id, 'acc_user');
|
|
201
|
+
assert.equal(result.registry.remote, false);
|
|
202
|
+
assert.equal(result.registry.reason, 'self_host_registry_isolated');
|
|
203
|
+
assert.ok(calls.some((call) => call.args.includes('deploy') && call.args.includes('--dry-run')));
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
test('buildProductRow stays on agentsam_products projection with deployment identity', () => {
|
|
43
208
|
const row = buildProductRow({
|
|
44
209
|
product: 'agentsam-go-worker',
|
|
45
|
-
repositoryId: 'agentsam-sdk',
|
|
210
|
+
repositoryId: 'github:samprimeaux/agentsam-sdk',
|
|
46
211
|
commit: 'abc',
|
|
47
212
|
url: 'https://x.workers.dev',
|
|
48
213
|
health: 'healthy',
|
|
214
|
+
workerDeploymentId: 'dep_1',
|
|
215
|
+
workerVersionId: 'ver_1',
|
|
216
|
+
artifactDigest: 'sha256:binary',
|
|
217
|
+
containerDigest: 'sha256:image',
|
|
49
218
|
});
|
|
50
219
|
assert.equal(row.slug, 'agentsam-go-worker');
|
|
51
220
|
assert.equal(row.kind, 'service');
|
|
221
|
+
assert.equal(row.status, 'deployed');
|
|
222
|
+
assert.equal(row.canonical_path, 'apps/agentsam-go-worker');
|
|
223
|
+
assert.equal(row.package_name, '@inneranimalmedia/agentsam-go-worker');
|
|
52
224
|
assert.equal(row.metadata.runtime, 'go');
|
|
225
|
+
assert.equal(row.metadata.deployment.worker_deployment_id, 'dep_1');
|
|
226
|
+
assert.equal(row.metadata.deployment.worker_version_id, 'ver_1');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('source identity produces a source-keyed Container instance', async () => {
|
|
230
|
+
const { runtimeInstanceKey } = await import('../../apps/agentsam-go-worker/worker/src/identity.js');
|
|
231
|
+
const first = runtimeInstanceKey('git:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
|
232
|
+
const second = runtimeInstanceKey('git:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
|
|
233
|
+
assert.equal(first, 'runtime:git:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
|
234
|
+
assert.equal(second, 'runtime:git:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
|
|
235
|
+
assert.notEqual(first, second);
|
|
236
|
+
assert.equal(runtimeInstanceKey('npm:@inneranimalmedia/agentsam-go-worker@0.1.0'), 'runtime:npm:@inneranimalmedia/agentsam-go-worker@0.1.0');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
test('validation-only Cloudflare runs do not replace production receipts', async () => {
|
|
241
|
+
const fs = await import('node:fs');
|
|
242
|
+
const os = await import('node:os');
|
|
243
|
+
|
|
244
|
+
const stateRoot = fs.mkdtempSync(
|
|
245
|
+
path.join(os.tmpdir(), 'agentsam-go-receipt-isolation-'),
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const goDir = path.join(stateRoot, '.agentsam', 'go');
|
|
249
|
+
fs.mkdirSync(goDir, { recursive: true });
|
|
250
|
+
|
|
251
|
+
const production = {
|
|
252
|
+
schema: 'agentsam.deployment-receipt.v1',
|
|
253
|
+
product: 'agentsam-go-worker',
|
|
254
|
+
provider: 'cloudflare',
|
|
255
|
+
url: 'https://runtime.inneranimalmedia.com',
|
|
256
|
+
health: 'healthy',
|
|
257
|
+
deployed_at: '2026-09-25T17:05:13.618296Z',
|
|
258
|
+
worker_deployment_id: 'dep_prod',
|
|
259
|
+
worker_version_id: 'ver_prod',
|
|
260
|
+
official_release: true,
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
fs.writeFileSync(
|
|
264
|
+
path.join(goDir, 'latest.deployment-receipt.json'),
|
|
265
|
+
JSON.stringify(production, null, 2) + '\n',
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const cloudflareIdentity = {
|
|
269
|
+
ok: true,
|
|
270
|
+
authenticated: true,
|
|
271
|
+
auth_type: 'OAuth Token',
|
|
272
|
+
account: { id: 'acc_test', name: 'Test', type: 'standard' },
|
|
273
|
+
accounts: [{ id: 'acc_test', name: 'Test', type: 'standard' }],
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const spawn = (command, args = []) => {
|
|
277
|
+
if (command === 'docker' && args[0] === 'info') {
|
|
278
|
+
return { status: 0, stdout: 'ok', stderr: '' };
|
|
279
|
+
}
|
|
280
|
+
if (args.includes('deploy') && args.includes('--dry-run')) {
|
|
281
|
+
return { status: 0, stdout: 'dry run', stderr: '' };
|
|
282
|
+
}
|
|
283
|
+
throw new Error('unexpected spawn: ' + [command, ...args].join(' '));
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
await deployGoCloudflare({
|
|
287
|
+
productRoot: PRODUCT_ROOT,
|
|
288
|
+
stateRoot,
|
|
289
|
+
product: 'agentsam-go-worker',
|
|
290
|
+
dryRun: true,
|
|
291
|
+
accountId: 'acc_test',
|
|
292
|
+
cloudflareIdentity,
|
|
293
|
+
source: {
|
|
294
|
+
identity: 'git:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
295
|
+
commit: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
296
|
+
package_name: '@inneranimalmedia/agentsam-go-worker',
|
|
297
|
+
package_version: '0.1.0',
|
|
298
|
+
},
|
|
299
|
+
artifactDigest: 'sha256:artifact',
|
|
300
|
+
containerDigest: 'sha256:container',
|
|
301
|
+
spawn,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const after = JSON.parse(
|
|
305
|
+
fs.readFileSync(
|
|
306
|
+
path.join(goDir, 'latest.deployment-receipt.json'),
|
|
307
|
+
'utf8',
|
|
308
|
+
),
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
assert.deepEqual(after, production);
|
|
312
|
+
assert.ok(
|
|
313
|
+
fs.existsSync(
|
|
314
|
+
path.join(goDir, 'latest.deployment-validation-receipt.json'),
|
|
315
|
+
),
|
|
316
|
+
);
|
|
53
317
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
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 test from 'node:test';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import {
|
|
8
|
+
GO_WORKER_PACKAGE,
|
|
9
|
+
discoverGoRuntime,
|
|
10
|
+
resolveProductRoot,
|
|
11
|
+
} from '../../src/go/discover.js';
|
|
12
|
+
|
|
13
|
+
const SDK_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
14
|
+
const PRODUCT_ROOT = path.join(SDK_ROOT, 'apps', 'agentsam-go-worker');
|
|
15
|
+
|
|
16
|
+
test('installed Go Worker package is discovered outside the monorepo', () => {
|
|
17
|
+
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-go-distribution-'));
|
|
18
|
+
const packageRoot = path.join(temp, 'node_modules', ...GO_WORKER_PACKAGE.split('/'));
|
|
19
|
+
fs.mkdirSync(path.dirname(packageRoot), { recursive: true });
|
|
20
|
+
fs.symlinkSync(PRODUCT_ROOT, packageRoot, 'dir');
|
|
21
|
+
|
|
22
|
+
const discovery = discoverGoRuntime(temp);
|
|
23
|
+
assert.equal(discovery.schema, 'agentsam.go-discovery.v2');
|
|
24
|
+
assert.equal(discovery.runtime?.origin, 'installed_package');
|
|
25
|
+
assert.equal(discovery.distribution?.package_name, GO_WORKER_PACKAGE);
|
|
26
|
+
assert.equal(discovery.distribution?.package_version, '0.1.0');
|
|
27
|
+
assert.equal(fs.realpathSync(resolveProductRoot(discovery)), fs.realpathSync(PRODUCT_ROOT));
|
|
28
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
assessAiAccess,
|
|
5
|
+
formatAiAccessOnboarding,
|
|
6
|
+
CF_OAUTH_REQUIRED_REDIRECTS,
|
|
7
|
+
LOCAL_STUDIO_LOGIN,
|
|
8
|
+
} from '../../src/models/ai-access-onboarding.js';
|
|
9
|
+
import { listModelCatalog } from '../../src/models/catalog.js';
|
|
10
|
+
import { getModelRecord, mergeModelReference, listModelCatalog as listFromIndex } from '../../src/models/index.js';
|
|
11
|
+
|
|
12
|
+
describe('listModelCatalog import surface', () => {
|
|
13
|
+
it('is defined from catalog and models/index', () => {
|
|
14
|
+
assert.equal(typeof listModelCatalog, 'function');
|
|
15
|
+
assert.equal(typeof listFromIndex, 'function');
|
|
16
|
+
assert.ok(listModelCatalog().length > 0);
|
|
17
|
+
assert.equal(typeof getModelRecord, 'function');
|
|
18
|
+
assert.equal(typeof mergeModelReference, 'function');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('ai access onboarding', () => {
|
|
23
|
+
it('marks ready when a provider credential is configured', () => {
|
|
24
|
+
const access = assessAiAccess({
|
|
25
|
+
inventory: { providers: [{ id: 'openai', configured: true }] },
|
|
26
|
+
ollama: { online: false, models: [] },
|
|
27
|
+
options: [{ value: 'none' }],
|
|
28
|
+
});
|
|
29
|
+
assert.equal(access.ready, true);
|
|
30
|
+
assert.equal(access.hasProviderKeys, true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('marks not ready and formats setup paths', () => {
|
|
34
|
+
const access = assessAiAccess({
|
|
35
|
+
inventory: { providers: [{ id: 'openai', configured: false }] },
|
|
36
|
+
ollama: { online: false, models: [] },
|
|
37
|
+
options: [{ value: 'none' }],
|
|
38
|
+
});
|
|
39
|
+
assert.equal(access.ready, false);
|
|
40
|
+
const text = formatAiAccessOnboarding(access, { wantCloudAi: true });
|
|
41
|
+
assert.match(text, /ollama\.com\/download/);
|
|
42
|
+
assert.match(text, /agentsam providers/);
|
|
43
|
+
assert.match(text, /api\/oauth\/cloudflare\/start/);
|
|
44
|
+
assert.ok(CF_OAUTH_REQUIRED_REDIRECTS.some((u) => u.includes('/api/oauth/cloudflare/callback')));
|
|
45
|
+
assert.match(LOCAL_STUDIO_LOGIN, /next=\/agentsam/);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -14,7 +14,7 @@ test('agentsam help is concise, topic-aware, and deterministic outside a TTY', (
|
|
|
14
14
|
assert.equal(help.status, 0, help.stderr);
|
|
15
15
|
assert.doesNotMatch(help.stderr, /Unknown command/);
|
|
16
16
|
assert.match(help.stdout, /Agent Sam/);
|
|
17
|
-
assert.match(help.stdout, /
|
|
17
|
+
assert.match(help.stdout, /SAM = Systematic Autonomous Machinery/);
|
|
18
18
|
assert.match(help.stdout, /agentsam help <topic>/);
|
|
19
19
|
assert.match(help.stdout, /press \/ for the command picker/i);
|
|
20
20
|
assert.doesNotMatch(help.stdout, /Inspect options:/);
|
|
@@ -143,13 +143,13 @@ test('homepage.html is decomposed into reusable site partials with relative link
|
|
|
143
143
|
assert.ok(header.length > 500, 'header.html must contain full header content');
|
|
144
144
|
assert.ok(footer.length > 500, 'footer.html must contain full footer content');
|
|
145
145
|
|
|
146
|
-
//
|
|
147
|
-
assert.match(header, /href="\/
|
|
148
|
-
assert.match(header, /href="\/
|
|
149
|
-
assert.match(header, /href="\/
|
|
150
|
-
assert.match(header, /href="\/
|
|
146
|
+
// AgentSam public header navigation is relative and product-specific (R2 partial SSOT).
|
|
147
|
+
assert.match(header, /href="\/"/);
|
|
148
|
+
assert.match(header, /href="\/learn\/"/);
|
|
149
|
+
assert.match(header, /href="\/themes\/"/);
|
|
150
|
+
assert.match(header, /href="\/packages\/sdk\/help\/"/);
|
|
151
151
|
assert.match(header, /href="\/auth\/login\?next=\/agentsam"/);
|
|
152
|
-
assert.doesNotMatch(header, /href="https:\/\/inneranimalmedia\.com\/(
|
|
152
|
+
assert.doesNotMatch(header, /href="https:\/\/inneranimalmedia\.com\/(learn|themes|packages\/sdk|agentsam)"/);
|
|
153
153
|
|
|
154
154
|
assert.match(footer, /href="\/work"/);
|
|
155
155
|
assert.match(footer, /href="\/about"/);
|