This commit is contained in:
Oleg Maslov
2026-09-02 10:14:22 +02:00
parent 0c3e2ead3b
commit b20b138fe4
771 changed files with 161561 additions and 9027 deletions

View File

@@ -0,0 +1,220 @@
import Fastify from 'fastify';
import { describe, expect, it, vi } from 'vitest';
import type { InstallResult, MarketplaceApprovalIdentity } from '@waggle/marketplace';
import {
CapabilityProposalStore,
createCapabilityProposalRoutes,
issueCapabilityProposalFromToolResult,
stripCapabilityRequestMarker,
} from '../../src/local/routes/capability-proposals.js';
const IDENTITY: MarketplaceApprovalIdentity = {
schemaVersion: 1,
packageId: 73,
sourceId: 4,
name: 'web-scraper',
publisher: 'Waggle Labs',
version: '2.1.0',
installType: 'skill',
manifestDigest: `sha256:${'a'.repeat(64)}`,
riskStatus: 'CLEAN',
riskScore: 100,
riskContentHash: 'approved-content',
riskBlocked: false,
riskDigest: `sha256:${'b'.repeat(64)}`,
};
const RAW_MARKER = '<!--waggle:capability_request {"name":"web-scraper","source":"marketplace","kind":"marketplace","packageId":73,"installType":"skill"}-->';
function installed(identity: MarketplaceApprovalIdentity): InstallResult {
return {
success: true,
packageId: identity.packageId,
packageName: identity.name,
installType: identity.installType,
installPath: '.waggle/skills/web-scraper.md',
message: 'Installed',
};
}
describe('capability proposal lifecycle', () => {
it('replaces a raw marketplace marker with a server-issued scoped proposal', async () => {
const store = new CapabilityProposalStore({ now: () => 1_000 });
const issued = await issueCapabilityProposalFromToolResult({
store,
workspaceId: 'workspace-a',
sessionId: 'session-a',
output: `Recommended.\n${RAW_MARKER}`,
resolveIdentity: vi.fn().mockResolvedValue(IDENTITY),
});
expect(issued.issued).toBe(true);
expect(issued.output).not.toContain(RAW_MARKER);
expect(issued.output).toContain('"proposalId"');
expect(issued.output).toContain('"publisher":"Waggle Labs"');
expect(issued.output).toContain('"expiresAt":"1970-01-01T00:10:01.000Z"');
});
it('strips an actionable marker when immutable identity cannot be resolved', async () => {
const store = new CapabilityProposalStore();
const issued = await issueCapabilityProposalFromToolResult({
store,
workspaceId: 'workspace-a',
sessionId: 'session-a',
output: `Unavailable.\n${RAW_MARKER}`,
resolveIdentity: vi.fn().mockResolvedValue(null),
});
expect(issued.issued).toBe(false);
expect(issued.output).not.toContain('waggle:capability_request');
});
it('strips rejected markers even when untrusted prose follows them', () => {
expect(stripCapabilityRequestMarker(`Before. ${RAW_MARKER} after.`))
.toBe('Before. after.');
const multiline = '<!--waggle:capability_request {\n"name":"web-scraper",\n"source":"marketplace"\n}-->';
expect(stripCapabilityRequestMarker(`Before. ${multiline} after.`))
.toBe('Before. after.');
});
it('claims once before awaiting so concurrent confirmation cannot double-install', async () => {
const store = new CapabilityProposalStore();
const proposal = store.issue('workspace-a', 'session-a', IDENTITY);
let release!: () => void;
const gate = new Promise<void>((resolve) => { release = resolve; });
const install = vi.fn(async (identity: MarketplaceApprovalIdentity) => {
await gate;
return installed(identity);
});
const first = store.confirm(proposal.id, 'workspace-a', 'session-a', install);
const second = await store.confirm(proposal.id, 'workspace-a', 'session-a', install);
release();
expect(second.status).toBe('used');
expect((await first).status).toBe('completed');
expect(install).toHaveBeenCalledTimes(1);
});
it('does not disclose or consume a proposal on workspace or session mismatch', async () => {
const store = new CapabilityProposalStore();
const proposal = store.issue('workspace-a', 'session-a', IDENTITY);
const install = vi.fn(async () => installed(IDENTITY));
expect((await store.confirm(proposal.id, 'workspace-b', 'session-a', install)).status)
.toBe('unavailable');
expect((await store.confirm(proposal.id, 'workspace-a', 'session-b', install)).status)
.toBe('unavailable');
expect((await store.confirm(proposal.id, 'workspace-a', 'session-a', install)).status)
.toBe('completed');
expect(install).toHaveBeenCalledTimes(1);
});
it('expires at the exact deadline without invoking installation', async () => {
let now = 5_000;
const store = new CapabilityProposalStore({ now: () => now, ttlMs: 600_000 });
const proposal = store.issue('workspace-a', 'session-a', IDENTITY);
const install = vi.fn(async () => installed(IDENTITY));
now = proposal.expiresAt;
expect((await store.confirm(proposal.id, 'workspace-a', 'session-a', install)).status)
.toBe('expired');
expect(install).not.toHaveBeenCalled();
});
it('keeps an identity-change failure terminal', async () => {
const store = new CapabilityProposalStore();
const proposal = store.issue('workspace-a', 'session-a', IDENTITY);
const install = vi.fn(async (): Promise<InstallResult> => ({
...installed(IDENTITY),
success: false,
errorCode: 'PACKAGE_IDENTITY_CHANGED',
message: 'Changed',
}));
const first = await store.confirm(proposal.id, 'workspace-a', 'session-a', install);
const replay = await store.confirm(proposal.id, 'workspace-a', 'session-a', install);
expect(first).toMatchObject({ status: 'completed', result: { success: false, errorCode: 'PACKAGE_IDENTITY_CHANGED' } });
expect(replay.status).toBe('used');
expect(install).toHaveBeenCalledTimes(1);
});
it('confirm route ignores client target and force substitutions', async () => {
const server = Fastify();
const store = new CapabilityProposalStore();
const proposal = store.issue('workspace-a', 'session-a', IDENTITY);
const install = vi.fn(async (identity: MarketplaceApprovalIdentity) => installed(identity));
await server.register(createCapabilityProposalRoutes(store, install));
await server.ready();
const response = await server.inject({
method: 'POST',
url: `/api/capability-proposals/${proposal.id}/confirm`,
payload: {
workspaceId: 'workspace-a',
sessionId: 'session-a',
packageId: 999,
force: true,
forceInsecure: true,
expectedApprovalIdentity: { ...IDENTITY, packageId: 999 },
},
});
expect(response.statusCode).toBe(200);
expect(install).toHaveBeenCalledWith(IDENTITY);
expect(install).toHaveBeenCalledTimes(1);
await server.close();
});
it('confirm route maps unavailable, expired, replay, and identity drift fail closed', async () => {
let now = 10_000;
const store = new CapabilityProposalStore({ now: () => now, ttlMs: 1_000 });
const install = vi.fn(async (): Promise<InstallResult> => ({
...installed(IDENTITY),
success: false,
errorCode: 'PACKAGE_IDENTITY_CHANGED',
message: 'Changed',
}));
const server = Fastify();
await server.register(createCapabilityProposalRoutes(store, install));
await server.ready();
const scoped = store.issue('workspace-a', 'session-a', IDENTITY);
const wrongScope = await server.inject({
method: 'POST',
url: `/api/capability-proposals/${scoped.id}/confirm`,
payload: { workspaceId: 'workspace-b', sessionId: 'session-a' },
});
expect(wrongScope.statusCode).toBe(404);
expect(wrongScope.json().code).toBe('CAPABILITY_PROPOSAL_NOT_AVAILABLE');
const drift = await server.inject({
method: 'POST',
url: `/api/capability-proposals/${scoped.id}/confirm`,
payload: { workspaceId: 'workspace-a', sessionId: 'session-a' },
});
expect(drift.statusCode).toBe(409);
expect(drift.json().errorCode).toBe('PACKAGE_IDENTITY_CHANGED');
const replay = await server.inject({
method: 'POST',
url: `/api/capability-proposals/${scoped.id}/confirm`,
payload: { workspaceId: 'workspace-a', sessionId: 'session-a' },
});
expect(replay.statusCode).toBe(409);
expect(replay.json().code).toBe('CAPABILITY_PROPOSAL_ALREADY_USED');
const expiring = store.issue('workspace-a', 'session-a', IDENTITY);
now = expiring.expiresAt;
const expired = await server.inject({
method: 'POST',
url: `/api/capability-proposals/${expiring.id}/confirm`,
payload: { workspaceId: 'workspace-a', sessionId: 'session-a' },
});
expect(expired.statusCode).toBe(410);
expect(expired.json().code).toBe('CAPABILITY_PROPOSAL_EXPIRED');
await server.close();
});
});