vouchington-tooling 0.45.0 → 0.46.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.
Files changed (88) hide show
  1. package/README.md +10 -52
  2. package/dist/agent-blackboard/feedback-audit-source.d.mts +4 -10
  3. package/dist/agent-blackboard/feedback-audit-source.mjs +7 -18
  4. package/dist/agent-blackboard/feedback-compose-audit.mjs +6 -14
  5. package/dist/agent-blackboard/feedback-compose.d.mts +1 -4
  6. package/dist/agent-blackboard/feedback-compose.mjs +2 -5
  7. package/dist/agent-blackboard/feedback-fields.mjs +1 -1
  8. package/dist/agent-blackboard/feedback-identity.mjs +1 -1
  9. package/dist/agent-blackboard/feedback-journal-audit.d.mts +6 -8
  10. package/dist/agent-blackboard/feedback-journal-audit.mjs +9 -10
  11. package/dist/agent-blackboard/feedback-journal-sandbox.d.mts +2 -2
  12. package/dist/agent-blackboard/feedback-journal-sandbox.mjs +2 -2
  13. package/dist/agent-blackboard/feedback-outbox-rejected.mjs +1 -1
  14. package/dist/agent-blackboard/feedback-outbox.mjs +2 -2
  15. package/dist/agent-blackboard/index.d.mts +1 -1
  16. package/dist/audit-journal/ci-failures.d.mts +6 -0
  17. package/dist/{session-friction → audit-journal}/ci-failures.mjs +3 -9
  18. package/dist/{session-friction → audit-journal}/journal.d.mts +0 -1
  19. package/dist/{session-friction → audit-journal}/journal.mjs +1 -1
  20. package/dist/audit-journal/session-id.d.mts +1 -0
  21. package/dist/{session-friction → audit-journal}/session-id.mjs +0 -5
  22. package/dist/{session-friction → audit-journal}/text.d.mts +1 -4
  23. package/dist/{session-friction → audit-journal}/text.mjs +4 -22
  24. package/dist/audit-journal/types.d.mts +17 -0
  25. package/dist/cli/commands/gha-runtime-audit.mjs +2 -0
  26. package/dist/gh-cli/env.d.mts +9 -0
  27. package/dist/gh-cli/env.mjs +12 -0
  28. package/dist/gh-cli/exec.d.mts +7 -4
  29. package/dist/gh-cli/exec.mjs +7 -5
  30. package/dist/gh-cli/index.d.mts +1 -0
  31. package/dist/gh-cli/index.mjs +1 -0
  32. package/dist/gha-post-review/github.mjs +2 -1
  33. package/dist/index.d.mts +3 -3
  34. package/dist/index.mjs +2 -2
  35. package/dist/mcp-server/context.d.mts +2 -0
  36. package/dist/mcp-server/dispatch.d.mts +2 -0
  37. package/dist/mcp-server/dispatch.mjs +1 -0
  38. package/dist/mcp-server/harness.test-helpers.d.mts +1 -0
  39. package/dist/mcp-server/harness.test-helpers.mjs +1 -0
  40. package/dist/mcp-server/pr-agent.d.mts +12 -0
  41. package/dist/mcp-server/pr-agent.mjs +34 -0
  42. package/dist/mcp-server/pr-body.d.mts +2 -0
  43. package/dist/mcp-server/pr-body.mjs +1 -2
  44. package/dist/mcp-server/pr-gh.mjs +2 -1
  45. package/dist/mcp-server/pr-input.d.mts +2 -1
  46. package/dist/mcp-server/pr-input.mjs +1 -1
  47. package/dist/mcp-server/pr-live.d.mts +1 -0
  48. package/dist/mcp-server/pr-live.mjs +1 -1
  49. package/dist/mcp-server/pr-prepare.mjs +25 -4
  50. package/dist/mcp-server/server.mjs +1 -0
  51. package/dist/mcp-server/tools-pr.mjs +8 -12
  52. package/dist/{session-friction → private-files}/lock.mjs +3 -2
  53. package/dist/private-files/log-file.d.mts +3 -0
  54. package/dist/private-files/log-file.mjs +53 -0
  55. package/dist/{session-friction → private-files}/utf8.mjs +1 -1
  56. package/dist/retrospective-facts/exec.mjs +2 -1
  57. package/docs/mcp-server.md +18 -4
  58. package/package.json +1 -6
  59. package/dist/session-friction/ci-failures.d.mts +0 -13
  60. package/dist/session-friction/classify.d.mts +0 -2
  61. package/dist/session-friction/classify.mjs +0 -98
  62. package/dist/session-friction/command-segments.d.mts +0 -3
  63. package/dist/session-friction/command-segments.mjs +0 -94
  64. package/dist/session-friction/coverage-ledger.d.mts +0 -10
  65. package/dist/session-friction/coverage-ledger.mjs +0 -75
  66. package/dist/session-friction/directory.d.mts +0 -1
  67. package/dist/session-friction/directory.mjs +0 -71
  68. package/dist/session-friction/index.d.mts +0 -6
  69. package/dist/session-friction/index.mjs +0 -5
  70. package/dist/session-friction/log-file.d.mts +0 -7
  71. package/dist/session-friction/log-file.mjs +0 -95
  72. package/dist/session-friction/log.d.mts +0 -8
  73. package/dist/session-friction/log.mjs +0 -163
  74. package/dist/session-friction/normalize.d.mts +0 -1
  75. package/dist/session-friction/normalize.mjs +0 -81
  76. package/dist/session-friction/report.d.mts +0 -16
  77. package/dist/session-friction/report.mjs +0 -91
  78. package/dist/session-friction/sandbox.d.mts +0 -3
  79. package/dist/session-friction/sandbox.mjs +0 -43
  80. package/dist/session-friction/session-id.d.mts +0 -2
  81. package/dist/session-friction/types.d.mts +0 -80
  82. /package/dist/{session-friction → audit-journal}/audit-block.d.mts +0 -0
  83. /package/dist/{session-friction → audit-journal}/audit-block.mjs +0 -0
  84. /package/dist/{session-friction → audit-journal}/audit-fit.d.mts +0 -0
  85. /package/dist/{session-friction → audit-journal}/audit-fit.mjs +0 -0
  86. /package/dist/{session-friction → audit-journal}/types.mjs +0 -0
  87. /package/dist/{session-friction → private-files}/lock.d.mts +0 -0
  88. /package/dist/{session-friction → private-files}/utf8.d.mts +0 -0
@@ -1,81 +0,0 @@
1
- import { REDACTED_TOKEN, splitCommand, stripAssignments } from "./command-segments.mjs";
2
- const MAX_COMMAND_TOKEN_LENGTH = 40;
3
- const MAX_COMMAND_LENGTH = 100_000;
4
- const MAX_COMMAND_WRAPPERS = 16;
5
- const CREDENTIAL_OPTION = /^--?(?:api[-_]?key|auth|credential|password|secret|token)=/i;
6
- const URL_USERINFO = /^[a-z][a-z0-9+.-]*:\/\/[^/@\s]+@/i;
7
- const PACKAGE_RUNNERS = new Set(['npm', 'npx', 'pnpm', 'pnpx', 'yarn']);
8
- const GIT_OPTIONS_WITH_ARGS = new Set(['-C', '-c', '--git-dir', '--work-tree']);
9
- const GIT_OPTIONS_WITHOUT_ARGS = new Set([
10
- '-v',
11
- '--version',
12
- '-h',
13
- '--help',
14
- '--exec-path',
15
- '--html-path',
16
- '--man-path',
17
- '--info-path',
18
- '-p',
19
- '--paginate',
20
- '-P',
21
- '--no-pager',
22
- '--no-replace-objects',
23
- '--bare',
24
- ]);
25
- function redact(token) {
26
- return token.length > MAX_COMMAND_TOKEN_LENGTH ||
27
- CREDENTIAL_OPTION.test(token) ||
28
- URL_USERINFO.test(token)
29
- ? REDACTED_TOKEN
30
- : token;
31
- }
32
- function stripGitOptions(tokens) {
33
- let index = 0;
34
- while (index < tokens.length) {
35
- const token = tokens[index];
36
- if (token === '--')
37
- return tokens.slice(index + 1);
38
- if (GIT_OPTIONS_WITH_ARGS.has(token)) {
39
- if (index + 1 >= tokens.length)
40
- return [REDACTED_TOKEN];
41
- index += 2;
42
- }
43
- else if (GIT_OPTIONS_WITHOUT_ARGS.has(token) || /^--[a-z0-9-]+=/i.test(token))
44
- index++;
45
- else
46
- break;
47
- }
48
- return tokens.slice(index);
49
- }
50
- function packageRunner(token) {
51
- return [...PACKAGE_RUNNERS].some((runner) => token === runner || token.endsWith(`/${runner}`));
52
- }
53
- function normalizeSegment(tokens) {
54
- const [first, ...rest] = tokens;
55
- if (!first)
56
- return '';
57
- if (packageRunner(first) && (rest[0] === 'run' || rest[0] === 'exec') && rest[1])
58
- return `${redact(first)} ${rest[0]} ${redact(rest[1])}`;
59
- const effective = first === 'git' || first.endsWith('/git') ? stripGitOptions(rest) : rest;
60
- return effective[0] ? `${redact(first)} ${redact(effective[0])}` : redact(first);
61
- }
62
- export function normalizeCommandPrefix(command, wrappersToStrip = []) {
63
- if (command.length > MAX_COMMAND_LENGTH)
64
- return '[REDACTED: command too long]';
65
- const segments = splitCommand(command);
66
- let index = 0;
67
- while (index < segments.length - 1 && stripAssignments(segments[index])[0] === 'cd')
68
- index++;
69
- const tokens = stripAssignments(segments[index] ?? []);
70
- const wrapperValues = Array.isArray(wrappersToStrip) ? wrappersToStrip : [];
71
- const wrapperNames = new Set(wrapperValues
72
- .slice(0, MAX_COMMAND_WRAPPERS)
73
- .filter((value) => typeof value === 'string' && value.length > 0 && value.length <= MAX_COMMAND_TOKEN_LENGTH));
74
- let wrappers = 0;
75
- while (tokens[wrappers] && wrapperNames.has(tokens[wrappers]))
76
- wrappers++;
77
- if (!wrappers)
78
- return normalizeSegment(tokens);
79
- const nested = normalizeSegment(stripAssignments(tokens.slice(wrappers)));
80
- return nested ? `${redact(tokens[0])} ${nested}` : redact(tokens[0]);
81
- }
@@ -1,16 +0,0 @@
1
- import { type JournalScan } from './journal.mts';
2
- import type { FrictionLogReadResult, SessionFrictionReport, SessionFrictionReportOptions } from './types.mts';
3
- type FrictionRead = {
4
- result: FrictionLogReadResult;
5
- } | {
6
- error: unknown;
7
- };
8
- /** Everything a report reads from outside, loaded once so rendering can be repeated for free. */
9
- export type FrictionReportInputs = {
10
- scanned: JournalScan;
11
- read: FrictionRead;
12
- };
13
- export declare function loadFrictionReportInputs(sessionId: string, options: SessionFrictionReportOptions): Promise<FrictionReportInputs>;
14
- export declare function renderFrictionReport(sessionId: string, options: SessionFrictionReportOptions, { scanned, read }: FrictionReportInputs): SessionFrictionReport;
15
- export declare function buildSessionFrictionReport(sessionId: string, options: SessionFrictionReportOptions): Promise<SessionFrictionReport>;
16
- export {};
@@ -1,91 +0,0 @@
1
- import { eventLimit, readFrictionLog, requireDirectory } from "./log.mjs";
2
- import { buildCiFailuresSection, getConformingGroups, incompleteCiSection } from "./ci-failures.mjs";
3
- import { errorMessage, scanJournal } from "./journal.mjs";
4
- import { buildSandboxSection } from "./sandbox.mjs";
5
- import { validateSessionId } from "./session-id.mjs";
6
- function sandboxMarkdown(friction, options) {
7
- if (friction.status === 'events')
8
- return `${friction.truncated ? `## Capture Coverage\nStatus: partial\nDropped records: ${friction.droppedCount}\n\n` : ''}${buildSandboxSection(friction.events, options.redact, options.budgets?.sandboxBytes)}`;
9
- if (friction.status === 'empty' && friction.truncated)
10
- return `## Sandbox & Permission Audit\nStatus: unavailable (partial capture; dropped ${friction.droppedCount} records)`;
11
- const status = friction.status === 'empty' ? 'none observed' : 'unavailable (no friction log)';
12
- return `## Sandbox & Permission Audit\nStatus: ${status}`;
13
- }
14
- function reportFromLog(sessionId, logOptions, journal, read) {
15
- if ('result' in read) {
16
- const friction = read.result;
17
- const coverage = {
18
- journalStatus: journal.status === 'unreachable'
19
- ? 'unavailable'
20
- : journal.truncated
21
- ? 'partial'
22
- : 'complete',
23
- frictionStatus: friction.status,
24
- truncated: friction.status !== 'absent' && friction.truncated === true,
25
- ...(friction.status === 'absent' ? {} : { droppedCount: friction.droppedCount ?? 0 }),
26
- };
27
- if (journal.status === 'ok' && journal.truncated)
28
- return {
29
- coverage,
30
- markdown: incompleteCiSection(journal.markdownBlocks) +
31
- '\n\n' +
32
- sandboxMarkdown(friction, logOptions),
33
- };
34
- const markdown = buildCiFailuresSection(sessionId, journal, friction.status);
35
- return { coverage, markdown: `${markdown}\n\n${sandboxMarkdown(friction, logOptions)}` };
36
- }
37
- else {
38
- const ciMarkdown = journal.status === 'ok' && journal.truncated
39
- ? incompleteCiSection(journal.markdownBlocks)
40
- : journal.status === 'ok' && journal.markdownBlocks.length === 0
41
- ? '## CI Failures\nStatus: unavailable (friction log unreadable)'
42
- : buildCiFailuresSection(sessionId, journal, 'empty');
43
- return {
44
- coverage: {
45
- journalStatus: journal.status === 'unreachable'
46
- ? 'unavailable'
47
- : journal.truncated
48
- ? 'partial'
49
- : 'complete',
50
- frictionStatus: 'unreadable',
51
- truncated: true,
52
- },
53
- markdown: `${ciMarkdown}\n\n## Sandbox & Permission Audit\nStatus: unavailable (friction log unreadable)`,
54
- diagnostic: errorMessage(read.error),
55
- };
56
- }
57
- }
58
- export async function loadFrictionReportInputs(sessionId, options) {
59
- validateSessionId(sessionId);
60
- requireDirectory(options.directory);
61
- eventLimit(options.maxEvents);
62
- const scanned = await scanJournal(sessionId, options.journalLoader);
63
- try {
64
- return { scanned, read: { result: readFrictionLog(sessionId, options) } };
65
- }
66
- catch (error) {
67
- return { scanned, read: { error } };
68
- }
69
- }
70
- export function renderFrictionReport(sessionId, options, { scanned, read }) {
71
- const journal = scanned.status === 'unreachable'
72
- ? scanned
73
- : scanned.status === 'not-found'
74
- ? { status: 'ok', markdownBlocks: [], truncated: false }
75
- : {
76
- status: 'ok',
77
- markdownBlocks: getConformingGroups(scanned.entries, options.redact, options.budgets?.ciBytes),
78
- truncated: scanned.truncated,
79
- };
80
- const report = reportFromLog(sessionId, options, journal, read);
81
- if (journal.status === 'unreachable') {
82
- const diagnostic = report.diagnostic
83
- ? `${journal.diagnostic}; ${report.diagnostic}`
84
- : journal.diagnostic;
85
- return { ...report, diagnostic };
86
- }
87
- return report;
88
- }
89
- export async function buildSessionFrictionReport(sessionId, options) {
90
- return renderFrictionReport(sessionId, options, await loadFrictionReportInputs(sessionId, options));
91
- }
@@ -1,3 +0,0 @@
1
- import type { FrictionEvent } from './types.mts';
2
- import { type AuditRedactor } from './text.mts';
3
- export declare function buildSandboxSection(events: FrictionEvent[], redact?: AuditRedactor, maxBytes?: number): string;
@@ -1,43 +0,0 @@
1
- import { byteLength, fitFields, selectWithin } from "./audit-fit.mjs";
2
- import { markdownAuditUnits } from "./text.mjs";
3
- const SANDBOX_SECTION_HEADER = '## Sandbox & Permission Audit';
4
- const SANDBOX_SECTION_MAX_BYTES = 3_000;
5
- // Room for the `- kind (n)` group lines, which are rendered outside the per-event budget.
6
- const GROUP_LINES_BYTES = 120;
7
- // Below this an event line would keep little beyond its timestamp, so it is omitted and counted.
8
- const MIN_EVENT_SHARE_BYTES = 120;
9
- // Event line fields: command prefix, detail, timestamp. Cut detail first, the timestamp never.
10
- const SHRINK_ORDER = [1, 0];
11
- const KIND_ORDER = [
12
- 'sandbox-escalation',
13
- 'sandbox-failure',
14
- 'ambiguous-failure',
15
- ];
16
- /** Reports dropped events as a group in the same shape as the rest, so omission is never silent. */
17
- function omissionGroup(count) {
18
- return `- omitted (${count})\n - see journal — ${count} events omitted to fit the size limit — n/a`;
19
- }
20
- export function buildSandboxSection(events, redact, maxBytes = SANDBOX_SECTION_MAX_BYTES) {
21
- const observations = events.flatMap((event) => event.failure
22
- ? [event, { ...event, kind: event.failure.kind, detail: event.failure.detail }]
23
- : [event]);
24
- const render = (event, budgetBytes) => {
25
- const outcome = event.kind === 'sandbox-escalation' ? ` — outcome: ${event.outcome ?? 'unknown'}` : '';
26
- const [prefix, detail, timestamp] = fitFields([event.commandPrefix, event.detail, event.timestamp].map((field) => markdownAuditUnits(field, redact)), byteLength(` - — — ${outcome}`), budgetBytes, SHRINK_ORDER);
27
- return ` - ${prefix} — ${detail} — ${timestamp}${outcome}`;
28
- };
29
- const total = Math.max(0, Math.min(maxBytes, SANDBOX_SECTION_MAX_BYTES) - GROUP_LINES_BYTES);
30
- const { rendered, omitted } = selectWithin(observations, total, byteLength(omissionGroup(observations.length)) + 1, 1, MIN_EVENT_SHARE_BYTES, render);
31
- const kept = observations
32
- .slice(0, rendered.length)
33
- .map((event, i) => ({ event, line: rendered[i] }));
34
- const groups = KIND_ORDER.map((kind) => {
35
- const selected = kept.filter(({ event }) => event.kind === kind);
36
- return selected.length
37
- ? [`- ${kind} (${selected.length})`, ...selected.map(({ line }) => line)].join('\n')
38
- : undefined;
39
- }).filter((group) => group !== undefined);
40
- if (omitted)
41
- groups.push(omissionGroup(omitted));
42
- return `${SANDBOX_SECTION_HEADER}\nEvents observed: ${observations.length}\n\n${groups.join('\n')}`;
43
- }
@@ -1,2 +0,0 @@
1
- export declare function validateSessionId(sessionId: string): void;
2
- export declare function sanitizeSessionId(sessionId: string): string;
@@ -1,80 +0,0 @@
1
- export type FrictionEventKind = 'sandbox-escalation' | 'sandbox-failure' | 'ambiguous-failure';
2
- export type FrictionEvent = {
3
- kind: FrictionEventKind;
4
- timestamp: string;
5
- commandPrefix: string;
6
- detail: string;
7
- outcome?: 'requested' | 'approved' | 'denied' | 'unknown';
8
- failure?: {
9
- kind: 'sandbox-failure' | 'ambiguous-failure';
10
- detail: string;
11
- };
12
- };
13
- export type ToolResultObservation = {
14
- type: 'tool-result';
15
- command: string;
16
- commandWrappers?: string[];
17
- permissionOutcome?: 'approved' | 'denied';
18
- escalationDetail?: string;
19
- structuredStderr?: string;
20
- };
21
- export type PermissionRequestObservation = {
22
- type: 'permission-request';
23
- command: string;
24
- commandWrappers?: string[];
25
- };
26
- export type FrictionObservation = ToolResultObservation | PermissionRequestObservation;
27
- type FrictionLogCoverage = {
28
- truncated: true;
29
- droppedCount: number;
30
- } | {
31
- truncated?: false;
32
- droppedCount?: never;
33
- };
34
- export type FrictionLogReadResult = {
35
- status: 'absent';
36
- } | ({
37
- status: 'empty';
38
- } & FrictionLogCoverage) | ({
39
- status: 'events';
40
- events: FrictionEvent[];
41
- } & FrictionLogCoverage);
42
- export type FrictionLogOptions = {
43
- directory: string;
44
- maxEvents?: number;
45
- };
46
- export type JournalEntry = {
47
- data?: {
48
- type?: unknown;
49
- markdown?: unknown;
50
- };
51
- };
52
- export type JournalLoadResult = {
53
- status: 'ok';
54
- entries: Iterable<JournalEntry> | AsyncIterable<JournalEntry>;
55
- } | {
56
- status: 'not-found';
57
- };
58
- export type JournalLoader = (sessionId: string) => JournalLoadResult | Promise<JournalLoadResult>;
59
- export type SessionFrictionCoverage = {
60
- journalStatus: 'complete' | 'partial' | 'unavailable';
61
- frictionStatus: 'absent' | 'empty' | 'events' | 'unreadable';
62
- truncated: boolean;
63
- droppedCount?: number;
64
- };
65
- export type SessionFrictionReport = {
66
- coverage: SessionFrictionCoverage;
67
- markdown: string;
68
- diagnostic?: string;
69
- };
70
- export type SessionFrictionReportOptions = FrictionLogOptions & {
71
- journalLoader: JournalLoader;
72
- /** Redacts raw field text before Markdown escaping, which would otherwise break matching. */
73
- redact?: (value: string) => string;
74
- /** Byte allowances for the CI and sandbox sections, as maximums over the built-in budgets. */
75
- budgets?: {
76
- ciBytes: number;
77
- sandboxBytes: number;
78
- };
79
- };
80
- export {};