kushi-agents 6.2.1 → 6.3.0

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.
@@ -1,92 +0,0 @@
1
- // plugin/runners/test/integration/pull-meetings.integration.test.mjs
2
-
3
- import { test, before, after } from 'node:test';
4
- import assert from 'node:assert/strict';
5
- import { promises as fs } from 'node:fs';
6
- import path from 'node:path';
7
- import os from 'node:os';
8
- import { spawnSync } from 'node:child_process';
9
- import YAML from 'yaml';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- const HERE = path.dirname(fileURLToPath(import.meta.url));
13
- const REPO_ROOT = path.resolve(HERE, '..', '..', '..', '..');
14
- const RUNNER = path.join(REPO_ROOT, 'plugin', 'runners', 'pull-meetings.mjs');
15
- const FIXTURE_OK = path.join(HERE, '..', 'fixtures', 'meetings-abn-amro.json');
16
- const FIXTURE_BU = path.join(HERE, '..', 'fixtures', 'meetings-body-unavailable.json');
17
- const JOIN_URL_OK = 'https://teams.microsoft.com/meet/31827546911768?p=YOgZHuaffeF0MGBCNt';
18
- const JOIN_URL_BU = 'https://teams.microsoft.com/meet/26457824867211?p=zdSjg6cqHoHPA8Hx11';
19
-
20
- let projectRoot;
21
-
22
- before(async () => {
23
- projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'kushi-meet-int-'));
24
- await fs.mkdir(path.join(projectRoot, 'Evidence', 'ushak'), { recursive: true });
25
- await fs.writeFile(path.join(projectRoot, 'integrations.yml'), YAML.stringify({}));
26
- });
27
- after(async () => { await fs.rm(projectRoot, { recursive: true, force: true }); });
28
-
29
- test('captures meeting + transcript verbatim', () => {
30
- const res = spawnSync(process.execPath, [RUNNER,
31
- '--project', projectRoot, '--alias', 'ushak',
32
- '--entity', JOIN_URL_OK, '--week', '2026-05-25', '--fixture', FIXTURE_OK,
33
- ], { encoding: 'utf8' });
34
- assert.equal(res.status, 0, `stderr: ${res.stderr}`);
35
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
36
- assert.equal(r.status, 'captured');
37
- assert.equal(r.items_pulled, 1);
38
- assert.ok(r.transcript_chars > 0);
39
- });
40
-
41
- test('transcript written verbatim to verbatim/transcript.txt', async () => {
42
- // Find the meetings directory (only one)
43
- const meetDir = path.join(projectRoot, 'Evidence', 'ushak', 'meetings');
44
- const entries = await fs.readdir(meetDir);
45
- assert.equal(entries.length, 1);
46
- const tx = await fs.readFile(path.join(meetDir, entries[0], 'verbatim', 'transcript.txt'), 'utf8');
47
- assert.match(tx, /WEBVTT/);
48
- assert.match(tx, /Architecture diagram is ready for review/);
49
- });
50
-
51
- test('body-unavailable → defers (4h marker) and writes meeting.yml only', async () => {
52
- const projectRoot2 = await fs.mkdtemp(path.join(os.tmpdir(), 'kushi-meet-bu-'));
53
- await fs.mkdir(path.join(projectRoot2, 'Evidence', 'ushak'), { recursive: true });
54
- await fs.writeFile(path.join(projectRoot2, 'integrations.yml'), YAML.stringify({}));
55
- try {
56
- const res = spawnSync(process.execPath, [RUNNER,
57
- '--project', projectRoot2, '--alias', 'ushak',
58
- '--entity', JOIN_URL_BU, '--week', '2026-05-25', '--fixture', FIXTURE_BU,
59
- ], { encoding: 'utf8' });
60
- assert.equal(res.status, 0, `stderr: ${res.stderr}`);
61
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
62
- assert.equal(r.status, 'body-unavailable');
63
- assert.equal(r.errors[0].signature, 'body-unavailable');
64
- assert.equal(r.errors[0].retry_after_min, 240);
65
- // deferred-retries directory has an entry
66
- const deferredDir = path.join(projectRoot2, 'Evidence', 'ushak', '_deferred-retries', 'meetings');
67
- const files = await fs.readdir(deferredDir);
68
- assert.ok(files.length >= 1);
69
- // No transcript file written
70
- const meetDir = path.join(projectRoot2, 'Evidence', 'ushak', 'meetings');
71
- const entries = await fs.readdir(meetDir);
72
- let txExists = true;
73
- try { await fs.access(path.join(meetDir, entries[0], 'verbatim', 'transcript.txt')); } catch { txExists = false; }
74
- assert.equal(txExists, false);
75
- } finally { await fs.rm(projectRoot2, { recursive: true, force: true }); }
76
- });
77
-
78
- test('ledger reflects captured state with transcript_chars', async () => {
79
- const ledger = YAML.parse(await fs.readFile(path.join(projectRoot, 'Evidence', 'ushak', '_ledger.yml'), 'utf8'));
80
- const cell = ledger.entries[`meetings::${JOIN_URL_OK}::2026-05-25`];
81
- assert.ok(cell);
82
- assert.equal(cell.last_status, 'captured');
83
- assert.equal(cell.transcript_available, true);
84
- assert.ok(cell.transcript_chars > 0);
85
- });
86
-
87
- test('missing --entity exits 2', () => {
88
- const res = spawnSync(process.execPath, [RUNNER,
89
- '--project', projectRoot, '--alias', 'ushak', '--fixture', FIXTURE_OK,
90
- ], { encoding: 'utf8' });
91
- assert.equal(res.status, 2);
92
- });
@@ -1,86 +0,0 @@
1
- // plugin/runners/test/integration/pull-onenote.integration.test.mjs
2
-
3
- import { test, before, after } from 'node:test';
4
- import assert from 'node:assert/strict';
5
- import { promises as fs } from 'node:fs';
6
- import path from 'node:path';
7
- import os from 'node:os';
8
- import { spawnSync } from 'node:child_process';
9
- import YAML from 'yaml';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- const HERE = path.dirname(fileURLToPath(import.meta.url));
13
- const REPO_ROOT = path.resolve(HERE, '..', '..', '..', '..');
14
- const RUNNER = path.join(REPO_ROOT, 'plugin', 'runners', 'pull-onenote.mjs');
15
- const FIXTURE_OK = path.join(HERE, '..', 'fixtures', 'onenote-abn-amro.json');
16
- const FIXTURE_PARTIAL = path.join(HERE, '..', 'fixtures', 'onenote-partial.json');
17
- const SECTION_ID = '015a0ac9-58f4-4f34-98b3-a60075b36627';
18
-
19
- let projectRoot;
20
-
21
- before(async () => {
22
- projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'kushi-on-int-'));
23
- await fs.mkdir(path.join(projectRoot, 'Evidence', 'ushak'), { recursive: true });
24
- await fs.writeFile(path.join(projectRoot, 'integrations.yml'), YAML.stringify({}));
25
- });
26
- after(async () => { await fs.rm(projectRoot, { recursive: true, force: true }); });
27
-
28
- test('captures all pages within the week, writes one HTML file per page', () => {
29
- const res = spawnSync(process.execPath, [RUNNER,
30
- '--project', projectRoot, '--alias', 'ushak',
31
- '--entity', SECTION_ID, '--week', '2026-05-25', '--fixture', FIXTURE_OK,
32
- ], { encoding: 'utf8' });
33
- assert.equal(res.status, 0, `stderr: ${res.stderr}`);
34
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
35
- assert.equal(r.status, 'captured');
36
- assert.equal(r.pages_enumerated, 2); // p1 + p2 (p3 is stale)
37
- assert.equal(r.items_pulled, 2);
38
- });
39
-
40
- test('per-page HTML written verbatim under content/<id>.html', async () => {
41
- const dir = path.join(projectRoot, 'Evidence', 'ushak', 'onenote', SECTION_ID, '2026-05-25', 'content');
42
- const p1 = await fs.readFile(path.join(dir, 'p1.html'), 'utf8');
43
- const p2 = await fs.readFile(path.join(dir, 'p2.html'), 'utf8');
44
- assert.match(p1, /Kickoff/);
45
- assert.match(p2, /Three-tier/);
46
- });
47
-
48
- test('partial: body-unavailable pages NOT enqueued in deferred-retries', async () => {
49
- const projectRoot2 = await fs.mkdtemp(path.join(os.tmpdir(), 'kushi-on-part-'));
50
- await fs.mkdir(path.join(projectRoot2, 'Evidence', 'ushak'), { recursive: true });
51
- await fs.writeFile(path.join(projectRoot2, 'integrations.yml'), YAML.stringify({}));
52
- try {
53
- const res = spawnSync(process.execPath, [RUNNER,
54
- '--project', projectRoot2, '--alias', 'ushak',
55
- '--entity', SECTION_ID, '--week', '2026-05-25', '--fixture', FIXTURE_PARTIAL,
56
- ], { encoding: 'utf8' });
57
- assert.equal(res.status, 0, `stderr: ${res.stderr}`);
58
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
59
- assert.equal(r.status, 'partial');
60
- assert.equal(r.items_pulled, 1);
61
- assert.deepEqual(r.body_unavailable, ['p2']);
62
- // Confirm OneNote is forbidden from enqueueing body-unavailable (lib/deferred.mjs throws)
63
- let deferredCount = 0;
64
- try {
65
- const files = await fs.readdir(path.join(projectRoot2, 'Evidence', 'ushak', '_deferred-retries', 'onenote'));
66
- deferredCount = files.length;
67
- } catch { deferredCount = 0; }
68
- assert.equal(deferredCount, 0);
69
- } finally { await fs.rm(projectRoot2, { recursive: true, force: true }); }
70
- });
71
-
72
- test('ledger captures pages_enumerated and section_file_id', async () => {
73
- const ledger = YAML.parse(await fs.readFile(path.join(projectRoot, 'Evidence', 'ushak', '_ledger.yml'), 'utf8'));
74
- const cell = ledger.entries[`onenote::${SECTION_ID}::2026-05-25`];
75
- assert.ok(cell);
76
- assert.equal(cell.last_status, 'captured');
77
- assert.equal(cell.pages_enumerated, 2);
78
- assert.equal(cell.section_file_id, SECTION_ID);
79
- });
80
-
81
- test('missing --entity exits 2', () => {
82
- const res = spawnSync(process.execPath, [RUNNER,
83
- '--project', projectRoot, '--alias', 'ushak', '--fixture', FIXTURE_OK,
84
- ], { encoding: 'utf8' });
85
- assert.equal(res.status, 2);
86
- });
@@ -1,93 +0,0 @@
1
- // plugin/runners/test/integration/pull-sharepoint.integration.test.mjs
2
-
3
- import { test, before, after } from 'node:test';
4
- import assert from 'node:assert/strict';
5
- import { promises as fs } from 'node:fs';
6
- import path from 'node:path';
7
- import os from 'node:os';
8
- import { spawnSync } from 'node:child_process';
9
- import YAML from 'yaml';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- const HERE = path.dirname(fileURLToPath(import.meta.url));
13
- const REPO_ROOT = path.resolve(HERE, '..', '..', '..', '..');
14
- const RUNNER = path.join(REPO_ROOT, 'plugin', 'runners', 'pull-sharepoint.mjs');
15
- const FIXTURE = path.join(HERE, '..', 'fixtures', 'sharepoint-abn-amro.json');
16
- const SITE_OK = 'https://abnamro.sharepoint.com/sites/TeamAlfred';
17
- const SITE_BAD = 'https://contoso-evil.sharepoint.com/sites/X';
18
-
19
- let projectRoot;
20
-
21
- before(async () => {
22
- projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'kushi-sp-int-'));
23
- await fs.mkdir(path.join(projectRoot, 'Evidence', 'ushak'), { recursive: true });
24
- await fs.writeFile(path.join(projectRoot, 'integrations.yml'), YAML.stringify({}));
25
- });
26
- after(async () => { await fs.rm(projectRoot, { recursive: true, force: true }); });
27
-
28
- test('captures items within ISO week — no bundling (one file per item)', async () => {
29
- const res = spawnSync(process.execPath, [RUNNER,
30
- '--project', projectRoot, '--alias', 'ushak',
31
- '--entity', SITE_OK, '--week', '2026-05-25',
32
- '--allowed-tenants', 'abnamro.sharepoint.com',
33
- '--fixture', FIXTURE,
34
- ], { encoding: 'utf8' });
35
- assert.equal(res.status, 0, `stderr: ${res.stderr}`);
36
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
37
- assert.equal(r.status, 'captured');
38
- assert.equal(r.items_pulled, 2);
39
- // No bundle file like items.yml — only one-file-per-item under items/
40
- const dir = path.join(projectRoot, 'Evidence', '_shared', 'sharepoint', r.site_hash, '2026-05-25');
41
- const itemFiles = await fs.readdir(path.join(dir, 'items'));
42
- assert.equal(itemFiles.length, 2);
43
- for (const f of itemFiles) assert.ok(f.endsWith('.yml'));
44
- });
45
-
46
- test('cross-tenant site blocked → status failed', async () => {
47
- const projectRoot2 = await fs.mkdtemp(path.join(os.tmpdir(), 'kushi-sp-xtenant-'));
48
- await fs.mkdir(path.join(projectRoot2, 'Evidence', 'ushak'), { recursive: true });
49
- await fs.writeFile(path.join(projectRoot2, 'integrations.yml'), YAML.stringify({}));
50
- try {
51
- const res = spawnSync(process.execPath, [RUNNER,
52
- '--project', projectRoot2, '--alias', 'ushak',
53
- '--entity', SITE_BAD, '--week', '2026-05-25',
54
- '--allowed-tenants', 'abnamro.sharepoint.com,microsoft.sharepoint.com',
55
- '--fixture', FIXTURE,
56
- ], { encoding: 'utf8' });
57
- assert.equal(res.status, 0);
58
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
59
- assert.equal(r.status, 'failed');
60
- assert.equal(r.errors[0].signature, 'cross-tenant-blocked');
61
- // No items written
62
- let exists = true;
63
- try { await fs.access(path.join(projectRoot2, 'Evidence', '_shared', 'sharepoint')); } catch { exists = false; }
64
- assert.equal(exists, false);
65
- } finally { await fs.rm(projectRoot2, { recursive: true, force: true }); }
66
- });
67
-
68
- test('item files have correct content', async () => {
69
- const r = JSON.parse(spawnSync(process.execPath, [RUNNER,
70
- '--project', projectRoot, '--alias', 'ushak',
71
- '--entity', SITE_OK, '--week', '2026-05-25',
72
- '--allowed-tenants', 'abnamro.sharepoint.com',
73
- '--fixture', FIXTURE,
74
- ], { encoding: 'utf8' }).stdout.trim().split('\n').pop());
75
- const dir = path.join(projectRoot, 'Evidence', '_shared', 'sharepoint', r.site_hash, '2026-05-25', 'items');
76
- const it = YAML.parse(await fs.readFile(path.join(dir, '01ABC.yml'), 'utf8'));
77
- assert.equal(it.name, 'Architecture.pptx');
78
- });
79
-
80
- test('ledger entry uses raw site url as cell key', async () => {
81
- const ledger = YAML.parse(await fs.readFile(path.join(projectRoot, 'Evidence', 'ushak', '_ledger.yml'), 'utf8'));
82
- const cell = ledger.entries[`sharepoint::${SITE_OK}::2026-05-25`];
83
- assert.ok(cell);
84
- assert.equal(cell.last_status, 'captured');
85
- assert.equal(cell.items_pulled, 2);
86
- });
87
-
88
- test('missing --entity exits 2', () => {
89
- const res = spawnSync(process.execPath, [RUNNER,
90
- '--project', projectRoot, '--alias', 'ushak', '--fixture', FIXTURE,
91
- ], { encoding: 'utf8' });
92
- assert.equal(res.status, 2);
93
- });
@@ -1,91 +0,0 @@
1
- // plugin/runners/test/integration/pull-teams.integration.test.mjs
2
-
3
- import { test, before, after } from 'node:test';
4
- import assert from 'node:assert/strict';
5
- import { promises as fs } from 'node:fs';
6
- import path from 'node:path';
7
- import os from 'node:os';
8
- import { spawnSync } from 'node:child_process';
9
- import YAML from 'yaml';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- const HERE = path.dirname(fileURLToPath(import.meta.url));
13
- const REPO_ROOT = path.resolve(HERE, '..', '..', '..', '..');
14
- const RUNNER = path.join(REPO_ROOT, 'plugin', 'runners', 'pull-teams.mjs');
15
- const FIXTURE = path.join(HERE, '..', 'fixtures', 'teams-abn-amro.json');
16
- const CHAT_ID = '19:e17bf64ba941442590dae0824cc4784c@thread.v2';
17
-
18
- let projectRoot;
19
-
20
- before(async () => {
21
- projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'kushi-teams-int-'));
22
- await fs.mkdir(path.join(projectRoot, 'Evidence', 'ushak'), { recursive: true });
23
- await fs.writeFile(path.join(projectRoot, 'integrations.yml'), YAML.stringify({}));
24
- });
25
- after(async () => { await fs.rm(projectRoot, { recursive: true, force: true }); });
26
-
27
- function runRunner(entity, extra = []) {
28
- return spawnSync(process.execPath, [RUNNER,
29
- '--project', projectRoot, '--alias', 'ushak',
30
- '--entity', entity,
31
- '--week', '2026-05-25', '--fixture', FIXTURE,
32
- ...extra,
33
- ], { encoding: 'utf8' });
34
- }
35
-
36
- test('captures only messages within the ISO week window', () => {
37
- const res = runRunner(CHAT_ID);
38
- assert.equal(res.status, 0, `stderr: ${res.stderr}`);
39
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
40
- assert.equal(r.status, 'captured');
41
- assert.equal(r.items_pulled, 3);
42
- assert.ok(r.chat_hash && r.chat_hash.length === 12);
43
- });
44
-
45
- test('uses shortHash for directory (unsafe chat id chars sanitized)', async () => {
46
- const r = JSON.parse(runRunner(CHAT_ID).stdout.trim().split('\n').pop());
47
- const dir = path.join(projectRoot, 'Evidence', 'ushak', 'teams', r.chat_hash, '2026-05-25');
48
- const msgs = YAML.parse(await fs.readFile(path.join(dir, 'messages.yml'), 'utf8'));
49
- assert.equal(msgs.length, 3);
50
- const chat = YAML.parse(await fs.readFile(path.join(dir, 'chat.yml'), 'utf8'));
51
- assert.equal(chat.chat_id, CHAT_ID);
52
- });
53
-
54
- test('ledger entry retains raw chat id as cell key', async () => {
55
- const ledger = YAML.parse(await fs.readFile(path.join(projectRoot, 'Evidence', 'ushak', '_ledger.yml'), 'utf8'));
56
- const cell = ledger.entries[`teams::${CHAT_ID}::2026-05-25`];
57
- assert.ok(cell);
58
- assert.equal(cell.last_status, 'captured');
59
- assert.equal(cell.items_pulled, 3);
60
- });
61
-
62
- test('empty chat returns no-activity', () => {
63
- const res = runRunner('19:empty@thread.v2');
64
- assert.equal(res.status, 0);
65
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
66
- assert.equal(r.status, 'no-activity');
67
- assert.equal(r.items_pulled, 0);
68
- assert.deepEqual(r.files_written, []);
69
- });
70
-
71
- test('index.md preview strips HTML', async () => {
72
- const r = JSON.parse(runRunner(CHAT_ID).stdout.trim().split('\n').pop());
73
- const md = await fs.readFile(path.join(projectRoot, 'Evidence', 'ushak', 'teams', r.chat_hash, '2026-05-25', 'index.md'), 'utf8');
74
- assert.match(md, /Status update/);
75
- assert.doesNotMatch(md, /<p>/);
76
- });
77
-
78
- test('dry-run writes nothing', async () => {
79
- const projectRoot2 = await fs.mkdtemp(path.join(os.tmpdir(), 'kushi-teams-dry-'));
80
- await fs.mkdir(path.join(projectRoot2, 'Evidence', 'ushak'), { recursive: true });
81
- await fs.writeFile(path.join(projectRoot2, 'integrations.yml'), YAML.stringify({}));
82
- try {
83
- const res = spawnSync(process.execPath, [RUNNER,
84
- '--project', projectRoot2, '--alias', 'ushak',
85
- '--entity', CHAT_ID, '--week', '2026-05-25', '--fixture', FIXTURE, '--dry-run',
86
- ], { encoding: 'utf8' });
87
- assert.equal(res.status, 0);
88
- const r = JSON.parse(res.stdout.trim().split('\n').pop());
89
- assert.deepEqual(r.files_written, []);
90
- } finally { await fs.rm(projectRoot2, { recursive: true, force: true }); }
91
- });