flow-external-agent-unit.test.mjs
85 lines 3.1 KB
Raw
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 11 hours ago
1 /**
2 * Tier 1 — UNIT: external-agent gate helpers, grant schema, harness gating.
3 */
4 import { describe, it, beforeEach, afterEach } from 'node:test';
5 import assert from 'node:assert/strict';
6 import fs from 'node:fs';
7 import path from 'node:path';
8 import { fileURLToPath } from 'node:url';
9
10 import {
11 getFlowExternalAgentEnabled,
12 collectFlowExternalToolRefs,
13 intersectGrantTools,
14 grantForClient,
15 hashGrantBearer,
16 FLOW_EXTERNAL_GRANT_SCHEMA,
17 } from '../lib/flow/external-agent.mjs';
18 import {
19 isHarnessActive,
20 isAgentBundleInert,
21 renderAgentBundle,
22 INERT_HARNESSES,
23 } from '../lib/flow/projection-generator.mjs';
24 import { writeExternalAgentPolicy, makeExternalToolFlowBundle } from './fixtures/flow/external-agent-helpers.mjs';
25
26 const __dirname = path.dirname(fileURLToPath(import.meta.url));
27 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-flow-external-agent-unit');
28
29 describe('Flow external-agent — unit', () => {
30 beforeEach(() => {
31 fs.rmSync(tmpRoot, { recursive: true, force: true });
32 fs.mkdirSync(tmpRoot, { recursive: true });
33 delete process.env.FLOW_EXTERNAL_AGENT_ENABLED;
34 });
35
36 afterEach(() => {
37 fs.rmSync(tmpRoot, { recursive: true, force: true });
38 delete process.env.FLOW_EXTERNAL_AGENT_ENABLED;
39 });
40
41 it('gate defaults off; policy file can enable', () => {
42 const dataDir = path.join(tmpRoot, 'off');
43 fs.mkdirSync(dataDir);
44 assert.equal(getFlowExternalAgentEnabled(dataDir), false);
45 writeExternalAgentPolicy(dataDir);
46 assert.equal(getFlowExternalAgentEnabled(dataDir), true);
47 });
48
49 it('isHarnessActive: agent_bundle only when gate on', () => {
50 assert.equal(isHarnessActive('agent_bundle'), false);
51 assert.equal(isHarnessActive('agent_bundle', { agentBundleEnabled: true }), true);
52 assert.equal(isAgentBundleInert(false), true);
53 assert.equal(isAgentBundleInert(true), false);
54 assert.ok(INERT_HARNESSES.has('agent_bundle'));
55 });
56
57 it('grantForClient never includes bearer hash', () => {
58 const stored = {
59 schema: FLOW_EXTERNAL_GRANT_SCHEMA,
60 grant_id: 'fgrnt_test',
61 grant_bearer_hash: hashGrantBearer('fgrnt_bearer_secret'),
62 };
63 const client = grantForClient(stored);
64 assert.equal(client.grant_bearer_hash, undefined);
65 assert.equal(JSON.stringify(client).includes('bearer'), false);
66 });
67
68 it('allowlist intersection is deterministic', () => {
69 const bundle = makeExternalToolFlowBundle();
70 const refs = collectFlowExternalToolRefs(bundle.steps);
71 const vault = new Set(['web_search', 'slack_notify']);
72 const out = intersectGrantTools(refs, vault, ['web_search', 'slack_notify']);
73 assert.deepEqual(out, ['web_search']);
74 });
75
76 it('renderAgentBundle validates JSON schema fields', () => {
77 const bundle = makeExternalToolFlowBundle();
78 const json = JSON.parse(renderAgentBundle(bundle.flow, bundle.steps, ['web_search']));
79 assert.equal(json.schema, 'knowtation.agent_bundle/v0');
80 assert.equal(json.grant_required, true);
81 assert.deepEqual(json.allowed_tools, ['web_search']);
82 assert.equal(json.steps.length, 1);
83 assert.equal(json.steps[0].skill_refs[0].kind, 'external_tool');
84 });
85 });
File History 1 commit
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 11 hours ago