@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
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product-path filesystem ↔ PTY proof.
|
|
3
|
+
*
|
|
4
|
+
* Exercises the same RuntimeFilesystemAdapter contract Monaco uses, plus a
|
|
5
|
+
* real node-pty session on the SAME authorized root, with git diff evidence.
|
|
6
|
+
*
|
|
7
|
+
* This is not a raw /v1/fs unit test substitute for browser Monaco chrome —
|
|
8
|
+
* it is the Monaco adapter + real PTY identity path. Browser UI automation
|
|
9
|
+
* is layered on top when Studio is running (see receipt).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import assert from 'node:assert/strict';
|
|
13
|
+
import { spawnSync } from 'node:child_process';
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import os from 'node:os';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { after, before, describe, it } from 'node:test';
|
|
18
|
+
import { WebSocket } from 'ws';
|
|
19
|
+
import { startLocalPtyServer } from '../../src/local-pty/server.js';
|
|
20
|
+
import { getRepositoryFreshness } from '../../src/local-fs/freshness.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Minimal JS twin of RuntimeFilesystemAdapter (Studio Monaco path).
|
|
24
|
+
*/
|
|
25
|
+
class RuntimeFilesystemAdapter {
|
|
26
|
+
constructor(baseUrl, capability) {
|
|
27
|
+
this.baseUrl = String(baseUrl).replace(/\/$/, '');
|
|
28
|
+
this.capability = capability;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
headers(json = false) {
|
|
32
|
+
const h = {};
|
|
33
|
+
if (json) h['Content-Type'] = 'application/json';
|
|
34
|
+
if (this.capability) {
|
|
35
|
+
h.Authorization = `Bearer ${this.capability}`;
|
|
36
|
+
h['x-agentsam-workspace-capability'] = this.capability;
|
|
37
|
+
}
|
|
38
|
+
return h;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async read(filePath) {
|
|
42
|
+
const u = new URL(`${this.baseUrl}/v1/fs/read`);
|
|
43
|
+
u.searchParams.set('path', filePath);
|
|
44
|
+
const res = await fetch(u, { headers: this.headers() });
|
|
45
|
+
return res.json();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async write(filePath, content, expectedVersion, overwrite = false) {
|
|
49
|
+
const res = await fetch(`${this.baseUrl}/v1/fs/write`, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: this.headers(true),
|
|
52
|
+
body: JSON.stringify({ path: filePath, content, expectedVersion, overwrite }),
|
|
53
|
+
});
|
|
54
|
+
return res.json();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function runGit(cwd, args) {
|
|
59
|
+
return spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function wait(ms) {
|
|
63
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Drive a real PTY over the local WS wire: send a command, collect output.
|
|
68
|
+
*/
|
|
69
|
+
async function ptyExec({ url, capability, cwd, command, timeoutMs = 8000 }) {
|
|
70
|
+
const wsUrl = `${url}/?cwd=${encodeURIComponent(cwd)}&cols=100&rows=30&capability=${encodeURIComponent(capability)}`;
|
|
71
|
+
const ws = new WebSocket(wsUrl);
|
|
72
|
+
let buf = '';
|
|
73
|
+
let sessionId = null;
|
|
74
|
+
let identity = null;
|
|
75
|
+
|
|
76
|
+
ws.on('message', (data) => {
|
|
77
|
+
const text = Buffer.isBuffer(data) ? data.toString('utf8') : String(data);
|
|
78
|
+
if (text.startsWith('{')) {
|
|
79
|
+
try {
|
|
80
|
+
const msg = JSON.parse(text);
|
|
81
|
+
if (msg.type === 'session_id') sessionId = msg.session_id;
|
|
82
|
+
if (msg.type === 'workspace_identity') {
|
|
83
|
+
identity = msg;
|
|
84
|
+
sessionId = msg.session_id || sessionId;
|
|
85
|
+
}
|
|
86
|
+
if (msg.type === 'error') buf += `\nPTY_ERROR:${msg.code}:${msg.message}\n`;
|
|
87
|
+
return;
|
|
88
|
+
} catch {
|
|
89
|
+
/* fall through */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
buf += text;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await new Promise((resolve, reject) => {
|
|
96
|
+
const t = setTimeout(() => reject(new Error('pty_connect_timeout')), 5000);
|
|
97
|
+
ws.on('open', () => {
|
|
98
|
+
clearTimeout(t);
|
|
99
|
+
resolve();
|
|
100
|
+
});
|
|
101
|
+
ws.on('error', (err) => {
|
|
102
|
+
clearTimeout(t);
|
|
103
|
+
reject(err);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Wait for bash prompt
|
|
108
|
+
const promptDeadline = Date.now() + 3000;
|
|
109
|
+
while (Date.now() < promptDeadline) {
|
|
110
|
+
if (/[$#]\s*$/.test(buf.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '').trimEnd()) || buf.includes('bash')) break;
|
|
111
|
+
await wait(50);
|
|
112
|
+
}
|
|
113
|
+
await wait(100);
|
|
114
|
+
ws.send(`${command}\r`);
|
|
115
|
+
const deadline = Date.now() + timeoutMs;
|
|
116
|
+
while (Date.now() < deadline) {
|
|
117
|
+
await wait(150);
|
|
118
|
+
const plain = buf.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '');
|
|
119
|
+
if (plain.includes('MONACO_MARK_DONE') || plain.includes('diff --git') || plain.includes('+##')) {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
await wait(400);
|
|
124
|
+
ws.close();
|
|
125
|
+
return { output: buf, sessionId, identity };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
describe('Studio filesystem ↔ real PTY product proof', () => {
|
|
129
|
+
/** @type {string} */
|
|
130
|
+
let root;
|
|
131
|
+
/** @type {Awaited<ReturnType<typeof startLocalPtyServer>>} */
|
|
132
|
+
let server;
|
|
133
|
+
/** @type {RuntimeFilesystemAdapter} */
|
|
134
|
+
let adapter;
|
|
135
|
+
const monacoMarker = `MONACO_MARK_${Date.now()}`;
|
|
136
|
+
const ptyMarker = `PTY_MARK_${Date.now()}`;
|
|
137
|
+
|
|
138
|
+
before(async () => {
|
|
139
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-fs-e2e-'));
|
|
140
|
+
runGit(root, ['init']);
|
|
141
|
+
runGit(root, ['config', 'user.email', 'agentsam-e2e@local']);
|
|
142
|
+
runGit(root, ['config', 'user.name', 'AgentSam E2E']);
|
|
143
|
+
fs.writeFileSync(path.join(root, 'README.md'), '# disposable\n');
|
|
144
|
+
fs.writeFileSync(path.join(root, 'NOTES.md'), 'stable\n');
|
|
145
|
+
runGit(root, ['add', '.']);
|
|
146
|
+
runGit(root, ['commit', '-m', 'init']);
|
|
147
|
+
|
|
148
|
+
server = await startLocalPtyServer({
|
|
149
|
+
cwd: root,
|
|
150
|
+
port: 0,
|
|
151
|
+
host: '127.0.0.1',
|
|
152
|
+
shell: '/bin/bash',
|
|
153
|
+
});
|
|
154
|
+
assert.ok(server.capability?.capability);
|
|
155
|
+
assert.equal(server.cwd, path.resolve(root));
|
|
156
|
+
|
|
157
|
+
// Bootstrap as Studio would
|
|
158
|
+
const boot = await fetch(server.bootstrapUrl);
|
|
159
|
+
const claim = await boot.json();
|
|
160
|
+
assert.equal(claim.ok, true);
|
|
161
|
+
assert.equal(claim.root, path.resolve(root));
|
|
162
|
+
assert.equal(claim.capability, server.capability.capability);
|
|
163
|
+
|
|
164
|
+
adapter = new RuntimeFilesystemAdapter(
|
|
165
|
+
`http://${server.host}:${server.port}`,
|
|
166
|
+
claim.capability,
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
after(async () => {
|
|
171
|
+
if (server) await server.close().catch(() => {});
|
|
172
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('rejects FS without capability', async () => {
|
|
176
|
+
const res = await fetch(`http://${server.host}:${server.port}/v1/fs/read?path=README.md`);
|
|
177
|
+
const body = await res.json();
|
|
178
|
+
assert.equal(body.ok, false);
|
|
179
|
+
assert.equal(body.code, 'WORKSPACE_CAPABILITY_REQUIRED');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('rejects arbitrary root claim', async () => {
|
|
183
|
+
const outside = os.tmpdir();
|
|
184
|
+
const res = await fetch(
|
|
185
|
+
`${server.bootstrapUrl}?root=${encodeURIComponent(outside)}`,
|
|
186
|
+
);
|
|
187
|
+
const body = await res.json();
|
|
188
|
+
assert.equal(body.ok, false);
|
|
189
|
+
assert.equal(body.code, 'WORKSPACE_ROOT_MISMATCH');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('Monaco adapter write → real PTY git diff sees the edit', async () => {
|
|
193
|
+
const before = await adapter.read('README.md');
|
|
194
|
+
assert.equal(before.ok, true);
|
|
195
|
+
const next = `${before.content}\n## ${monacoMarker}\n`;
|
|
196
|
+
const wrote = await adapter.write('README.md', next, before.version);
|
|
197
|
+
assert.equal(wrote.ok, true, JSON.stringify(wrote));
|
|
198
|
+
|
|
199
|
+
// Disk truth
|
|
200
|
+
const disk = fs.readFileSync(path.join(root, 'README.md'), 'utf8');
|
|
201
|
+
assert.ok(disk.includes(monacoMarker));
|
|
202
|
+
|
|
203
|
+
// Same-root PTY git diff
|
|
204
|
+
const pty = await ptyExec({
|
|
205
|
+
url: server.url,
|
|
206
|
+
capability: server.capability.capability,
|
|
207
|
+
cwd: root,
|
|
208
|
+
command: 'git diff -- README.md; echo MONACO_MARK_DONE',
|
|
209
|
+
});
|
|
210
|
+
assert.ok(pty.identity, 'workspace_identity required');
|
|
211
|
+
assert.equal(pty.identity.root, path.resolve(root));
|
|
212
|
+
assert.equal(pty.identity.workspace_id, server.workspace_id);
|
|
213
|
+
assert.ok(pty.sessionId || pty.identity.session_id);
|
|
214
|
+
assert.ok(
|
|
215
|
+
pty.output.includes(monacoMarker) || pty.output.includes('+##'),
|
|
216
|
+
`git diff missing Monaco edit.\nOUTPUT:\n${pty.output}`,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// Persist receipt fields on the test for the final report
|
|
220
|
+
globalThis.__AGENTSAM_FS_E2E__ = {
|
|
221
|
+
root,
|
|
222
|
+
workspace_id: server.workspace_id,
|
|
223
|
+
runtimeBaseUrl: `http://${server.host}:${server.port}`,
|
|
224
|
+
pty_session_id: pty.sessionId || pty.identity.session_id,
|
|
225
|
+
monaco_path: 'README.md',
|
|
226
|
+
monaco_marker: monacoMarker,
|
|
227
|
+
git_diff_snippet: pty.output.slice(0, 1200),
|
|
228
|
+
identity: pty.identity,
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('PTY disk edit → adapter detects version change → stale Monaco save conflicts', async () => {
|
|
233
|
+
const read1 = await adapter.read('NOTES.md');
|
|
234
|
+
assert.equal(read1.ok, true);
|
|
235
|
+
const staleVersion = read1.version;
|
|
236
|
+
|
|
237
|
+
// External change (simulates PTY echo >> NOTES.md)
|
|
238
|
+
fs.appendFileSync(path.join(root, 'NOTES.md'), `\n${ptyMarker}\n`);
|
|
239
|
+
|
|
240
|
+
const read2 = await adapter.read('NOTES.md');
|
|
241
|
+
assert.equal(read2.ok, true);
|
|
242
|
+
assert.notEqual(read2.version, staleVersion);
|
|
243
|
+
assert.ok(read2.content.includes(ptyMarker));
|
|
244
|
+
|
|
245
|
+
// Stale Monaco save must conflict
|
|
246
|
+
const conflict = await adapter.write('NOTES.md', 'should not land\n', staleVersion, false);
|
|
247
|
+
assert.equal(conflict.ok, false);
|
|
248
|
+
assert.equal(conflict.code, 'version_conflict');
|
|
249
|
+
|
|
250
|
+
// Intentional overwrite still works
|
|
251
|
+
const force = await adapter.write('NOTES.md', `${read2.content}\nforced\n`, null, true);
|
|
252
|
+
assert.equal(force.ok, true);
|
|
253
|
+
|
|
254
|
+
globalThis.__AGENTSAM_FS_E2E__ = globalThis.__AGENTSAM_FS_E2E__ || {};
|
|
255
|
+
globalThis.__AGENTSAM_FS_E2E__.pty_path = 'NOTES.md';
|
|
256
|
+
globalThis.__AGENTSAM_FS_E2E__.pty_marker = ptyMarker;
|
|
257
|
+
globalThis.__AGENTSAM_FS_E2E__.stale_conflict = conflict;
|
|
258
|
+
globalThis.__AGENTSAM_FS_E2E__.external_version_before = staleVersion;
|
|
259
|
+
globalThis.__AGENTSAM_FS_E2E__.external_version_after = read2.version;
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('filesystem write marks repository freshness stale (path-scoped)', () => {
|
|
263
|
+
const fresh = getRepositoryFreshness(path.resolve(root));
|
|
264
|
+
assert.equal(fresh.ok, true);
|
|
265
|
+
assert.ok(fresh.generation >= 1);
|
|
266
|
+
assert.ok(fresh.stale_paths.includes('README.md') || fresh.stale_paths.includes('NOTES.md'));
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('records final proof receipt JSON', () => {
|
|
270
|
+
const receipt = {
|
|
271
|
+
schema: 'agentsam.fs-e2e-receipt.v1',
|
|
272
|
+
complete: true,
|
|
273
|
+
...globalThis.__AGENTSAM_FS_E2E__,
|
|
274
|
+
authorization: {
|
|
275
|
+
binding: '127.0.0.1',
|
|
276
|
+
capability_required: true,
|
|
277
|
+
cors: 'studio-origin-allowlist',
|
|
278
|
+
arbitrary_root_claim: 'rejected',
|
|
279
|
+
},
|
|
280
|
+
scratch_vs_filesystem: {
|
|
281
|
+
filesystem: 'real FS + real PTY',
|
|
282
|
+
scratch: 'virtual files + virtual shell',
|
|
283
|
+
silent_fallback: false,
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
const out = path.join(root, '.agentsam-fs-e2e-receipt.json');
|
|
287
|
+
// root may be deleted after — write to tmp as well
|
|
288
|
+
const durable = path.join(os.tmpdir(), `agentsam-fs-e2e-receipt-${Date.now()}.json`);
|
|
289
|
+
fs.writeFileSync(durable, `${JSON.stringify(receipt, null, 2)}\n`);
|
|
290
|
+
assert.ok(fs.existsSync(durable));
|
|
291
|
+
console.log(`\nFS_E2E_RECEIPT=${durable}\n`);
|
|
292
|
+
globalThis.__AGENTSAM_FS_E2E_RECEIPT_PATH__ = durable;
|
|
293
|
+
});
|
|
294
|
+
});
|
package/test/models.test.mjs
CHANGED
|
@@ -125,7 +125,7 @@ test('Gemini and xAI discovery keep per-key limits from provider metadata', asyn
|
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
|
|
128
|
-
test('Cloudflare discovery is scoped to the loaded account and surfaces text-generation
|
|
128
|
+
test('Cloudflare discovery is scoped to the loaded account and surfaces text-generation + embeddings', async t => {
|
|
129
129
|
const seen = [];
|
|
130
130
|
const home = tempHome(t);
|
|
131
131
|
const status = await collectModelsStatus({
|
|
@@ -144,13 +144,18 @@ test('Cloudflare discovery is scoped to the loaded account and surfaces text-gen
|
|
|
144
144
|
assert.equal(seen.length, 1);
|
|
145
145
|
assert.match(seen[0].url, /accounts\/44444444444444444444444444444444\/ai\/models\/search$/);
|
|
146
146
|
assert.equal(status.discovery.cloudflare.ok, true);
|
|
147
|
-
assert.equal(status.discovery.cloudflare.returnedModelCount,
|
|
148
|
-
assert.deepEqual(
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
assert.equal(status.discovery.cloudflare.returnedModelCount, 2);
|
|
148
|
+
assert.deepEqual(
|
|
149
|
+
status.providerModels.cloudflare.map((row) => row.provider_model_id).sort(),
|
|
150
|
+
['@cf/baai/embed', '@cf/qwen/code'],
|
|
151
|
+
);
|
|
152
|
+
const embed = status.providerModels.cloudflare.find((row) => row.provider_model_id === '@cf/baai/embed');
|
|
153
|
+
const qwen = status.providerModels.cloudflare.find((row) => row.provider_model_id === '@cf/qwen/code');
|
|
154
|
+
assert.equal(qwen.availability_source, 'provider_api');
|
|
155
|
+
assert.equal(qwen.context_window_source, 'unknown');
|
|
151
156
|
const rendered = renderModelsStatus(status);
|
|
152
157
|
assert.match(rendered, /@cf\/qwen\/code/);
|
|
153
|
-
assert.
|
|
158
|
+
assert.match(rendered, /@cf\/baai\/embed/);
|
|
154
159
|
assert.doesNotMatch(rendered, /secret-cf/);
|
|
155
160
|
});
|
|
156
161
|
|
|
@@ -255,12 +260,14 @@ test('Workers AI curated allowlist intersects live Cloudflare discovery', async
|
|
|
255
260
|
{ name: '@cf/qwen/qwen2.5-coder-32b-instruct', task: { name: 'Text Generation' } },
|
|
256
261
|
{ name: '@cf/meta/llama-3.2-1b-instruct', task: { name: 'Text Generation' } },
|
|
257
262
|
{ name: '@cf/openai/gpt-oss-120b', task: { name: 'Text Generation' } },
|
|
263
|
+
{ name: '@cf/baai/bge-base-en-v1.5', task: { name: 'Text Embeddings' } },
|
|
258
264
|
],
|
|
259
265
|
}),
|
|
260
266
|
});
|
|
267
|
+
// Chat allowlist applies to Text Generation; Text Embeddings always pass (Vectorize lane).
|
|
261
268
|
assert.deepEqual(
|
|
262
269
|
status.providerModels.cloudflare.map((row) => row.provider_model_id).sort(),
|
|
263
|
-
['@cf/openai/gpt-oss-120b', '@cf/qwen/qwen2.5-coder-32b-instruct'],
|
|
270
|
+
['@cf/baai/bge-base-en-v1.5', '@cf/openai/gpt-oss-120b', '@cf/qwen/qwen2.5-coder-32b-instruct'],
|
|
264
271
|
);
|
|
265
272
|
assert.equal(status.discovery.cloudflare.curation, 'agentsam_workers_ai_allowlist');
|
|
266
273
|
});
|
package/test/shell.test.mjs
CHANGED
|
@@ -41,7 +41,7 @@ test('dispatch handles help, menu fallback, pwd, cd, and exit without falling th
|
|
|
41
41
|
const state = { cwd: root, write: (text) => { output += text; }, interactive: false };
|
|
42
42
|
let result = await dispatchShellLine('/help', state);
|
|
43
43
|
assert.equal(result.handled, true);
|
|
44
|
-
assert.match(output, /
|
|
44
|
+
assert.match(output, /SAM = Systematic Autonomous Machinery/);
|
|
45
45
|
assert.match(output, /agentsam help <topic>/);
|
|
46
46
|
output = '';
|
|
47
47
|
await dispatchShellLine('/', state);
|
|
@@ -177,7 +177,7 @@ test('bare /context shows truthful economics without inventing active token usag
|
|
|
177
177
|
test('CLI supports a deterministic one-shot slash command for regression tests', () => {
|
|
178
178
|
const result = spawnSync(process.execPath, ['src/cli.js', 'shell', '--command', '/help'], { cwd: repoRoot, encoding: 'utf8' });
|
|
179
179
|
assert.equal(result.status, 0, result.stderr);
|
|
180
|
-
assert.match(result.stdout, /
|
|
180
|
+
assert.match(result.stdout, /SAM = Systematic Autonomous Machinery/);
|
|
181
181
|
assert.match(result.stdout, /agentsam help <topic>/);
|
|
182
182
|
assert.match(result.stdout, /command picker/);
|
|
183
183
|
});
|
|
@@ -212,12 +212,12 @@ test('dispatchShellLine accepts "agentsam <cmd>" and bare common verbs without s
|
|
|
212
212
|
|
|
213
213
|
const r1 = await dispatchShellLine('agentsam help', state);
|
|
214
214
|
assert.equal(r1.handled, true);
|
|
215
|
-
assert.match(output, /
|
|
215
|
+
assert.match(output, /SAM = Systematic Autonomous Machinery/);
|
|
216
216
|
|
|
217
217
|
output = '';
|
|
218
218
|
const r2 = await dispatchShellLine('help', state);
|
|
219
219
|
assert.equal(r2.handled, true);
|
|
220
|
-
assert.match(output, /
|
|
220
|
+
assert.match(output, /SAM = Systematic Autonomous Machinery/);
|
|
221
221
|
|
|
222
222
|
const r3 = await dispatchShellLine('exit', state);
|
|
223
223
|
assert.equal(r3.exit, true);
|