agent-gov-core 0.4.3 → 0.7.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.
package/dist/toml.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import { toConfigParseError } from './parse-error.js';
2
3
  export function readTomlObject(path) {
3
4
  const text = readFileSync(path, 'utf8');
4
5
  try {
@@ -6,7 +7,7 @@ export function readTomlObject(path) {
6
7
  return { value: parsed, toml: parsed, text };
7
8
  }
8
9
  catch (err) {
9
- return { value: undefined, toml: undefined, text, parseError: err };
10
+ return { value: undefined, toml: undefined, text, parseError: toConfigParseError(text, err) };
10
11
  }
11
12
  }
12
13
  /**
@@ -122,11 +123,11 @@ class TomlParser {
122
123
  // TOML spec violation. Without this guard, `[foo]` silently descended
123
124
  // into the last `[[foo]]` entry and let writes leak into it.
124
125
  if (this.aotPaths.has(path)) {
125
- throw new Error(`Cannot redefine array-of-tables [[${keys.join('.')}]] as a standard table [${keys.join('.')}]`);
126
+ throw new Error(`Cannot redefine array-of-tables [[${keys.join('.')}]] as a standard table [${keys.join('.')}] at offset ${this.pos}`);
126
127
  }
127
128
  const table = this.descendTablePath(keys, /*forHeader*/ true);
128
129
  if (this.definedTables.has(path)) {
129
- throw new Error(`Duplicate table definition: [${keys.join('.')}]`);
130
+ throw new Error(`Duplicate table definition: [${keys.join('.')}] at offset ${this.pos}`);
130
131
  }
131
132
  this.definedTables.add(path);
132
133
  this.current = table;
@@ -153,6 +154,17 @@ class TomlParser {
153
154
  else if (!Array.isArray(arr)) {
154
155
  throw new Error(`Key ${keys.join('.')} is not an array-of-tables`);
155
156
  }
157
+ // Each new array entry resets the "already defined" status of any subtables
158
+ // declared under this AOT path. TOML spec permits the same subtable header
159
+ // (`[fruits.physical]`) to reappear under each fresh `[[fruits]]` entry — it
160
+ // binds to the current array entry. Without this clearing, the v0.4.2
161
+ // definedTables guard rejected the second [fruits.physical] as a duplicate.
162
+ const aotPathPrefix = keys.join(this.PATH_KEY_SEPARATOR) + this.PATH_KEY_SEPARATOR;
163
+ for (const definedPath of this.definedTables) {
164
+ if (definedPath.startsWith(aotPathPrefix)) {
165
+ this.definedTables.delete(definedPath);
166
+ }
167
+ }
156
168
  const newTable = {};
157
169
  arr.push(newTable);
158
170
  this.current = newTable;
@@ -248,7 +260,7 @@ class TomlParser {
248
260
  }
249
261
  const lastKey = keys[keys.length - 1];
250
262
  if (Object.prototype.hasOwnProperty.call(node, lastKey)) {
251
- throw new Error(`Duplicate key: ${keys.join('.')}`);
263
+ throw new Error(`Duplicate key: ${keys.join('.')} at offset ${this.pos}`);
252
264
  }
253
265
  node[lastKey] = value;
254
266
  this.expectLineEnd();
@@ -322,9 +334,18 @@ class TomlParser {
322
334
  const c = this.src[this.pos];
323
335
  if (c === '\\') {
324
336
  this.pos++;
325
- // line-ending backslash: consume to next non-ws line start
326
- const next = this.src[this.pos];
337
+ // Line-ending backslash: per TOML spec, a `\` followed by *any amount
338
+ // of inline whitespace* (spaces/tabs) and then a newline strips the
339
+ // newline and trims leading whitespace on the next line. Peek past
340
+ // trailing inline whitespace before deciding whether this is a
341
+ // line-ending backslash or a regular escape.
342
+ let peek = this.pos;
343
+ while (peek < this.len && (this.src[peek] === ' ' || this.src[peek] === '\t')) {
344
+ peek++;
345
+ }
346
+ const next = this.src[peek];
327
347
  if (next === '\n' || next === '\r' || next === undefined) {
348
+ this.pos = peek;
328
349
  while (this.pos < this.len &&
329
350
  (this.src[this.pos] === ' ' ||
330
351
  this.src[this.pos] === '\t' ||
@@ -480,7 +501,7 @@ class TomlParser {
480
501
  // Without this guard, `{ host = "a", host = "b" }` silently parsed as
481
502
  // `{ host: "b" }` instead of raising.
482
503
  if (Object.prototype.hasOwnProperty.call(node, leaf)) {
483
- throw new Error(`Duplicate key in inline table: ${keys.join('.')}`);
504
+ throw new Error(`Duplicate key in inline table: ${keys.join('.')} at offset ${this.pos}`);
484
505
  }
485
506
  node[leaf] = value;
486
507
  this.skipInlineWhitespace();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-gov-core",
3
- "version": "0.4.3",
3
+ "version": "0.7.0",
4
4
  "description": "Shared primitives for the AI-agent governance suite: Finding schema, JSONC/TOML readers, line locators, MCP command normalization, shell tokenization, and GitHub Action helpers.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,7 +14,8 @@
14
14
  "types": "./dist/test-utils.d.ts",
15
15
  "import": "./dist/test-utils.js"
16
16
  },
17
- "./schemas/finding.schema.json": "./schemas/finding.schema.json"
17
+ "./schemas/finding.schema.json": "./schemas/finding.schema.json",
18
+ "./schemas/report.schema.json": "./schemas/report.schema.json"
18
19
  },
19
20
  "files": [
20
21
  "dist/**/*.js",
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/Conalh/agent-gov-core/schemas/report.schema.json",
4
+ "title": "Report",
5
+ "description": "Canonical multi-tool report envelope emitted by tools in the AI-agent governance suite. Wraps a `Finding[]` with provenance, rating, and optional tool-specific extension data so a cross-tool meta-reviewer can ingest reports from N tools through one shape.",
6
+ "type": "object",
7
+ "required": ["schemaVersion", "tool", "rating", "findings"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "schemaVersion": {
11
+ "type": "string",
12
+ "const": "1.0",
13
+ "description": "Envelope schema version. Currently '1.0'. Future incompatible envelope changes require a major bump on this field; readers must reject unknown major versions."
14
+ },
15
+ "tool": {
16
+ "type": "string",
17
+ "enum": ["scope_trail", "policy_mesh", "capability_echo", "task_bound", "session_trail"],
18
+ "description": "Originating tool. Matches the `Finding.tool` enum."
19
+ },
20
+ "toolVersion": {
21
+ "type": "string",
22
+ "description": "Semver of the emitting tool (e.g. '0.1.18'). Optional; helps a meta-reviewer attribute findings to a specific release."
23
+ },
24
+ "runId": {
25
+ "type": "string",
26
+ "description": "Unique identifier for this run. Optional; useful when merging reports from concurrent runs or comparing runs over time."
27
+ },
28
+ "conversationId": {
29
+ "type": "string",
30
+ "description": "Identifier for the agent session, PR review, or thread this run belongs to. Distinct from runId (one conversation can produce many runs). Matches OpenTelemetry's gen_ai.conversation.id semantic convention — if a consumer also emits OTel traces about the same agent session, pass the same string here."
31
+ },
32
+ "baseRef": {
33
+ "type": "string",
34
+ "description": "Git base ref for diff-mode tools (ScopeTrail, CapabilityEcho, TaskBound). Omit for non-diff tools (PolicyMesh, SessionTrail)."
35
+ },
36
+ "headRef": {
37
+ "type": "string",
38
+ "description": "Git head ref for diff-mode tools. Omit for non-diff tools."
39
+ },
40
+ "rating": {
41
+ "type": "string",
42
+ "enum": ["none", "low", "medium", "high", "critical"],
43
+ "description": "Aggregate severity for this report. 'none' iff `findings` is empty or all findings fell below the tool's fail-on threshold."
44
+ },
45
+ "findings": {
46
+ "type": "array",
47
+ "items": { "$ref": "./finding.schema.json" },
48
+ "description": "Findings emitted by this tool run."
49
+ },
50
+ "data": {
51
+ "type": "object",
52
+ "description": "Tool-specific extension data (e.g. PolicyMesh's `effectiveUnion`, CapabilityEcho's `surfaceSummary`, ScopeTrail's `scopeMatchCount`). Opaque to a cross-tool meta-reviewer but preserved so each tool's UX can read its own extensions back from a merged report."
53
+ }
54
+ }
55
+ }