@tryinget/pi-agent-vent 0.2.0 → 0.2.1

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.
@@ -48,6 +48,9 @@ Rules:
48
48
  - `toolbox` activation only makes the `agent_vent` tool callable; it does not create or preview a diagnostic record.
49
49
  - `agent_vent action=preview` sanitizes the would-be record and runs the anti-junk quality check without writing the store.
50
50
  - `agent_vent action=record` writes only if the local quality check accepts the payload.
51
+ - `self.diagnostic_candidate.v1` exposes separate `agentVentSuggestionAllowed` and `agentVentRecordAllowed` permissions. Plain `no agent_vent` / `no-agent_vent` constraints suppress activation, preview, and record guidance. Record-only `no agent_vent record` / `no-agent_vent-record` constraints preserve non-mutating preview guidance while marking durable recording forbidden.
52
+ - ASC category `context_alignment` is accepted by `agent_vent` as a compatibility alias and normalized to canonical category `other`; the alias does not add a new canonical category.
53
+ - The toolbox bundle currently exposes only its `default` diagnostic profile. Keep a call read-only by choosing `agent_vent` actions such as `preview`, `review`, or `stats`; do not invent `profile=read`.
51
54
  - Generic low-signal summaries such as `done` are rejected for `record`; use `preview` to inspect issues and warnings before writing.
52
55
  - Human/operator judgment still decides whether any recurrence should become an AK task, GitHub issue, incident review, evidence record, publication, or owner-surface handoff.
53
56
 
@@ -1,3 +1,9 @@
1
+ // ---
2
+ // summary: registers the agent_vent tool and command for local diagnostic review workflows
3
+ // read_when:
4
+ // - changing agent vent actions, command parsing, tool schemas, or authority boundaries
5
+ // ---
6
+
1
7
  import path from "node:path";
2
8
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
9
  import { Type } from "typebox";
@@ -92,6 +98,7 @@ const CATEGORY_ALIAS_INPUTS = [
92
98
  "docs_gap",
93
99
  "context_window",
94
100
  "context_friction",
101
+ "context_alignment",
95
102
  "tooling_friction",
96
103
  ] as const;
97
104
  const CATEGORY_INPUTS = [...CATEGORIES, ...CATEGORY_ALIAS_INPUTS] as const;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryinget/pi-agent-vent",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Local pi tool for agents to record recurring frustrations, bugs, and workflow friction without creating incidents or external telemetry",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -87,8 +87,8 @@
87
87
  },
88
88
  "devDependencies": {
89
89
  "@biomejs/biome": "2.3.14",
90
- "@earendil-works/pi-ai": "0.80.6",
91
- "@earendil-works/pi-coding-agent": "0.80.6"
90
+ "@earendil-works/pi-ai": "0.83.0",
91
+ "@earendil-works/pi-coding-agent": "0.83.0"
92
92
  },
93
93
  "overrides": {
94
94
  "fast-xml-parser": "5.3.6"
@@ -99,6 +99,6 @@
99
99
  "typebox": "*"
100
100
  },
101
101
  "dependencies": {
102
- "typebox": "*"
102
+ "typebox": "1.3.7"
103
103
  }
104
104
  }
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env bash
2
+ # ---
3
+ # summary: locates and invokes documentation listing support for pi-agent-vent
4
+ # read_when:
5
+ # - enumerating package docs or overriding the docs-list implementation
6
+ # ---
2
7
  set -euo pipefail
3
8
 
4
9
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env bash
2
+ # ---
3
+ # summary: runs pi-agent-vent quality stages through the root gate or package fallback
4
+ # read_when:
5
+ # - checking lint, types, tests, or scaffold structure in varied checkout layouts
6
+ # ---
2
7
  set -euo pipefail
3
8
 
4
9
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@@ -1,3 +1,9 @@
1
+ // ---
2
+ // summary: validates packed file whitelists and local links in published markdown
3
+ // read_when:
4
+ // - changing pi-agent-vent package contents or release artifact policy
5
+ // ---
6
+
1
7
  import fs from "node:fs";
2
8
  import path from "node:path";
3
9
 
@@ -10,7 +16,7 @@ export const assertSafePackPath = (filePath) => {
10
16
  return filePath;
11
17
  };
12
18
 
13
- export const parsePackJson = (packJsonText) => {
19
+ export const parsePackJson = (packJsonText, expectedIdentity = null) => {
14
20
  let pack;
15
21
  try {
16
22
  pack = JSON.parse(packJsonText || "[]");
@@ -18,11 +24,30 @@ export const parsePackJson = (packJsonText) => {
18
24
  throw new Error(`Could not parse npm pack --dry-run --json output: ${error.message}`);
19
25
  }
20
26
 
21
- if (!Array.isArray(pack) || !pack[0] || !Array.isArray(pack[0].files)) {
27
+ let packEntry = null;
28
+ if (Array.isArray(pack) && pack.length === 1) {
29
+ [packEntry] = pack;
30
+ } else if (pack && typeof pack === "object") {
31
+ const entries = Object.entries(pack);
32
+ if (entries.length === 1 && entries[0][0] === entries[0][1]?.name) {
33
+ packEntry = entries[0][1];
34
+ }
35
+ }
36
+ if (!packEntry || !Array.isArray(packEntry.files)) {
22
37
  throw new Error("Could not parse npm pack --dry-run --json output.");
23
38
  }
39
+ if (
40
+ expectedIdentity &&
41
+ (packEntry.name !== expectedIdentity.name ||
42
+ packEntry.version !== expectedIdentity.version ||
43
+ packEntry.id !== `${expectedIdentity.name}@${expectedIdentity.version}`)
44
+ ) {
45
+ throw new Error(
46
+ `npm pack identity mismatch: expected ${expectedIdentity.name}@${expectedIdentity.version}.`,
47
+ );
48
+ }
24
49
 
25
- return pack[0].files
50
+ return packEntry.files
26
51
  .map((file) => normalizePackPath(String(file.path || "")))
27
52
  .filter(Boolean)
28
53
  .map(assertSafePackPath)
@@ -182,8 +207,13 @@ export const runReleaseArtifactCheck = ({
182
207
  cwd = process.cwd(),
183
208
  log = console.log,
184
209
  }) => {
185
- const actualFiles = parsePackJson(packJsonText);
186
- const filesEntries = readPackageFilesContract(path.join(cwd, "package.json"));
210
+ const manifestPath = path.join(cwd, "package.json");
211
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
212
+ const actualFiles = parsePackJson(packJsonText, {
213
+ name: manifest.name,
214
+ version: manifest.version,
215
+ });
216
+ const filesEntries = readPackageFilesContract(manifestPath);
187
217
  const whitelist = validatePackageFilesWhitelist({ actualFiles, filesEntries, cwd });
188
218
 
189
219
  if (whitelist.missing.length || whitelist.extra.length) {
@@ -1,9 +1,42 @@
1
1
  #!/usr/bin/env bash
2
+ # ---
3
+ # summary: exercises pi-agent-vent packaging, isolated installation, and release smoke gates
4
+ # read_when:
5
+ # - preparing a release or investigating tarball and pi installation failures
6
+ # ---
2
7
  set -euo pipefail
3
8
 
4
9
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
10
+ REPO_ROOT="$(cd "$ROOT_DIR/../.." && pwd)"
5
11
  cd "$ROOT_DIR"
6
12
 
13
+ # Scratch-root resolution, from first principles:
14
+ # the invariant is "release-check scratch lands on a managed, disposable root",
15
+ # NOT "the TMPDIR variable happens to be exported". A GitHub-hosted runner is
16
+ # single-tenant and ephemeral; its RUNNER_TEMP is exactly a managed scratch
17
+ # root for that job, so CI satisfies the invariant even though runners never
18
+ # export TMPDIR. Locally the strict rule is unchanged: no TMPDIR, no run.
19
+ if [[ -n "${TMPDIR:-}" ]]; then
20
+ TMP_ROOT="$TMPDIR"
21
+ elif [[ "${GITHUB_ACTIONS:-}" == "true" && -n "${RUNNER_TEMP:-}" && -d "${RUNNER_TEMP:-}" ]]; then
22
+ TMP_ROOT="$RUNNER_TEMP"
23
+ else
24
+ echo "TMPDIR must name the managed scratch root for release checks" >&2
25
+ exit 1
26
+ fi
27
+ mkdir -p "$TMP_ROOT"
28
+ TMP_ROOT="$(cd "$TMP_ROOT" && pwd -P)"
29
+ case "$TMP_ROOT" in
30
+ /|"$ROOT_DIR"|"$ROOT_DIR"/*)
31
+ echo "Release check refused unsafe TMPDIR: $TMP_ROOT" >&2
32
+ exit 1
33
+ ;;
34
+ esac
35
+ release_tmp_dir() {
36
+ local label="$1"
37
+ mktemp -d "$TMP_ROOT/pi-agent-vent-${label}-XXXXXX"
38
+ }
39
+
7
40
  NAME="$(node -p "JSON.parse(require('node:fs').readFileSync('package.json', 'utf8')).name")"
8
41
  VERSION="$(node -p "JSON.parse(require('node:fs').readFileSync('package.json', 'utf8')).version")"
9
42
  REPOSITORY_URL="$(node -p "(() => { const pkg = JSON.parse(require('node:fs').readFileSync('package.json', 'utf8')); const repo = pkg.repository; if (typeof repo === 'string') return repo.trim(); if (repo && typeof repo === 'object' && typeof repo.url === 'string') return repo.url.trim(); return ''; })()")"
@@ -18,7 +51,7 @@ with_release_npm_policy() {
18
51
  local prefix="$2"
19
52
  shift 2
20
53
  case "$cache" in
21
- /tmp/pi-agent-vent-*-npm-cache-*) ;;
54
+ "$TMP_ROOT"/pi-agent-vent-*-npm-cache-*) ;;
22
55
  *)
23
56
  echo "Release command refused non-isolated npm cache: $cache" >&2
24
57
  return 1
@@ -26,7 +59,7 @@ with_release_npm_policy() {
26
59
  esac
27
60
  if [[ "$prefix" != "-" ]]; then
28
61
  case "$prefix" in
29
- /tmp/pi-agent-vent-*-npm-prefix-*) ;;
62
+ "$TMP_ROOT"/pi-agent-vent-*-npm-prefix-*) ;;
30
63
  *)
31
64
  echo "Release command refused non-isolated npm prefix: $prefix" >&2
32
65
  return 1
@@ -50,10 +83,10 @@ release_npm_install() {
50
83
  local prefix="$2"
51
84
  shift 2
52
85
  with_release_npm_policy "$cache" "$prefix" npm install \
53
- --ignore-scripts --no-audit --fund=false "$@"
86
+ --include=optional --ignore-scripts --no-audit --fund=false "$@"
54
87
  }
55
88
 
56
- CONTROL_NPM_CACHE="$(mktemp -d /tmp/pi-agent-vent-control-npm-cache-XXXXXX)"
89
+ CONTROL_NPM_CACHE="$(release_tmp_dir control-npm-cache)"
57
90
  TEST_AGENT_DIR=""
58
91
  TEST_NPM_PREFIX=""
59
92
  TEST_NPM_CACHE=""
@@ -97,6 +130,9 @@ fi
97
130
  echo "== npm pack --dry-run --json"
98
131
  PACK_JSON="$(npm --cache "$CONTROL_NPM_CACHE" pack --dry-run --json)"
99
132
  echo "$PACK_JSON"
133
+ if [[ -f "$REPO_ROOT/scripts/npm-pack-json.mjs" ]]; then
134
+ PACK_JSON="$(printf '%s' "$PACK_JSON" | node "$REPO_ROOT/scripts/npm-pack-json.mjs")"
135
+ fi
100
136
 
101
137
  PACK_JSON="$PACK_JSON" node ./scripts/release-artifact-check.mjs
102
138
 
@@ -120,8 +156,8 @@ TARBALL="$(npm --cache "$CONTROL_NPM_CACHE" pack --silent | tail -n 1)"
120
156
  TARBALL_PATH="$ROOT_DIR/$TARBALL"
121
157
  echo "Tarball: $TARBALL_PATH"
122
158
 
123
- TARBALL_CHECK_DIR="$(mktemp -d /tmp/pi-agent-vent-tarball-check-XXXXXX)"
124
- TARBALL_NPM_CACHE="$(mktemp -d /tmp/pi-agent-vent-tarball-npm-cache-XXXXXX)"
159
+ TARBALL_CHECK_DIR="$(release_tmp_dir tarball-check)"
160
+ TARBALL_NPM_CACHE="$(release_tmp_dir tarball-npm-cache)"
125
161
  echo "== unpacked tarball package contract"
126
162
  tar -xzf "$TARBALL_PATH" -C "$TARBALL_CHECK_DIR"
127
163
  (
@@ -132,9 +168,9 @@ tar -xzf "$TARBALL_PATH" -C "$TARBALL_CHECK_DIR"
132
168
  npm run check
133
169
  )
134
170
 
135
- ARTIFACT_NPM_PREFIX="$(mktemp -d /tmp/pi-agent-vent-artifact-npm-prefix-XXXXXX)"
136
- ARTIFACT_NPM_CACHE="$(mktemp -d /tmp/pi-agent-vent-artifact-npm-cache-XXXXXX)"
137
- ARTIFACT_TOOL_VENT_DIR="$(mktemp -d /tmp/pi-agent-vent-artifact-tool-store-XXXXXX)"
171
+ ARTIFACT_NPM_PREFIX="$(release_tmp_dir artifact-npm-prefix)"
172
+ ARTIFACT_NPM_CACHE="$(release_tmp_dir artifact-npm-cache)"
173
+ ARTIFACT_TOOL_VENT_DIR="$(release_tmp_dir artifact-tool-store)"
138
174
  ARTIFACT_PACKAGE_ROOT="$(npm --prefix "$ARTIFACT_NPM_PREFIX" root -g)/$NAME"
139
175
 
140
176
  case "$ARTIFACT_PACKAGE_ROOT" in
@@ -173,9 +209,9 @@ else
173
209
  exit 1
174
210
  fi
175
211
 
176
- TEST_AGENT_DIR="$(mktemp -d /tmp/pi-agent-vent-pi-agent-dir-XXXXXX)"
177
- TEST_NPM_PREFIX="$(mktemp -d /tmp/pi-agent-vent-pi-npm-prefix-XXXXXX)"
178
- TEST_NPM_CACHE="$(mktemp -d /tmp/pi-agent-vent-pi-npm-cache-XXXXXX)"
212
+ TEST_AGENT_DIR="$(release_tmp_dir pi-agent-dir)"
213
+ TEST_NPM_PREFIX="$(release_tmp_dir pi-npm-prefix)"
214
+ TEST_NPM_CACHE="$(release_tmp_dir pi-npm-cache)"
179
215
 
180
216
  cp "$HOME/.pi/agent/auth.json" "$TEST_AGENT_DIR/auth.json"
181
217
 
@@ -1,3 +1,9 @@
1
+ // ---
2
+ // summary: supplies assertions and isolated tool-path probes for release smoke testing
3
+ // read_when:
4
+ // - changing installed-artifact validation or agent_vent release smoke behavior
5
+ // ---
6
+
1
7
  import fs from "node:fs";
2
8
  import os from "node:os";
3
9
  import path from "node:path";
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env bash
2
+ # ---
3
+ # summary: smoke-tests the installed agent_vent package through isolated pi discovery
4
+ # read_when:
5
+ # - verifying tarball installation, command output, or registered-tool execution
6
+ # ---
2
7
  set -euo pipefail
3
8
 
4
9
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ // ---
3
+ // summary: enforces pi-agent-vent manifest, script, extension, and lane structure contracts
4
+ // read_when:
5
+ // - editing package metadata, copier answers, publish files, or required npm scripts
6
+ // ---
2
7
 
3
8
  import fs from "node:fs";
4
9
 
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env bash
2
+ # ---
3
+ # summary: verifies the pi-agent-vent scaffold tree, executables, and markdown frontmatter
4
+ # read_when:
5
+ # - checking filesystem conformance after package scaffold or documentation changes
6
+ # ---
2
7
  set -euo pipefail
3
8
 
4
9
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
package/src/vent-store.js CHANGED
@@ -1,3 +1,9 @@
1
+ // ---
2
+ // summary: implements private agent vent storage, review projections, and retention safeguards
3
+ // read_when:
4
+ // - changing diagnostic record schemas, redaction, curation, review, export, or retention logic
5
+ // ---
6
+
1
7
  import { createHash, randomUUID } from "node:crypto";
2
8
  import fs from "node:fs";
3
9
  import os from "node:os";
@@ -32,6 +38,7 @@ export const CATEGORY_ALIASES = Object.freeze({
32
38
  docs_gap: "documentation",
33
39
  context_window: "context_loss",
34
40
  context_friction: "context_loss",
41
+ context_alignment: "other",
35
42
  tooling_friction: "friction",
36
43
  });
37
44
  export const SEVERITIES = ["low", "medium", "high", "critical"];
@@ -1,3 +1,9 @@
1
+ // ---
2
+ // summary: tests agent_vent tool registration, commands, actions, and authority-safe output
3
+ // read_when:
4
+ // - modifying the extension schema, command surface, lifecycle actions, or smoke contracts
5
+ // ---
6
+
1
7
  import assert from "node:assert/strict";
2
8
  import fs from "node:fs";
3
9
  import os from "node:os";
@@ -46,6 +52,7 @@ test("agent_vent tool schema stays aligned with retention candidate and compare
46
52
  assert.match(schemaText, /"compare"/);
47
53
  assert.match(schemaText, /"preview"/);
48
54
  assert.match(schemaText, /"workflow_friction"/);
55
+ assert.match(schemaText, /"context_alignment"/);
49
56
  assert.match(schemaText, /"candidates"/);
50
57
  assert.match(schemaText, /"history"/);
51
58
  assert.match(schemaText, /retentionCandidateState/);
@@ -549,6 +556,23 @@ test("agent_vent accepts category aliases for tool records and command filters",
549
556
  "workflow:workflow-friction-alias-should-normalize",
550
557
  );
551
558
 
559
+ const alignmentPreview = await tool.execute(
560
+ "tool-call-context-alignment-alias",
561
+ {
562
+ action: "preview",
563
+ summary: "Context alignment candidate should remain callable",
564
+ category: "context_alignment",
565
+ },
566
+ undefined,
567
+ undefined,
568
+ {
569
+ cwd: "/repo",
570
+ sessionManager: { getSessionFile: () => undefined },
571
+ },
572
+ );
573
+ assert.equal(alignmentPreview.details.wouldRecord, true);
574
+ assert.equal(alignmentPreview.details.recordPreview.category, "other");
575
+
552
576
  const reviewResult = await tool.execute(
553
577
  "tool-call-category-alias-review",
554
578
  { action: "review", category: "workflow_friction" },
@@ -1,3 +1,9 @@
1
+ // ---
2
+ // summary: tests package whitelist parsing and published markdown link validation
3
+ // read_when:
4
+ // - changing release artifact checks, pack output handling, or files-array policy
5
+ // ---
6
+
1
7
  import assert from "node:assert/strict";
2
8
  import fs from "node:fs";
3
9
  import os from "node:os";
@@ -77,6 +83,28 @@ test("pack json parser fails closed for malformed, stale, or unsafe pack output"
77
83
  );
78
84
  });
79
85
 
86
+ test("pack json parser accepts canonical arrays and npm 12 package-keyed objects", () => {
87
+ const entry = { name: "@tryinget/pi-agent-vent", files: [{ path: "package.json" }] };
88
+ assert.deepEqual(parsePackJson(JSON.stringify([entry])), ["package.json"]);
89
+ assert.deepEqual(parsePackJson(JSON.stringify({ "@tryinget/pi-agent-vent": entry })), [
90
+ "package.json",
91
+ ]);
92
+ assert.throws(
93
+ () =>
94
+ parsePackJson(
95
+ JSON.stringify({
96
+ "@tryinget/pi-agent-vent": entry,
97
+ "@tryinget/other": entry,
98
+ }),
99
+ ),
100
+ /Could not parse npm pack/,
101
+ );
102
+ assert.throws(
103
+ () => parsePackJson(JSON.stringify({ "@tryinget/wrong": entry })),
104
+ /Could not parse npm pack/,
105
+ );
106
+ });
107
+
80
108
  test("package files whitelist fails closed for wildcard entries", () => {
81
109
  assert.throws(
82
110
  () =>
@@ -114,13 +142,19 @@ test("package files whitelist detects missing and extra artifact entries", () =>
114
142
  test("release artifact check accepts directory files entries and always-included metadata", () => {
115
143
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-vent-release-check-ok-"));
116
144
  try {
117
- fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ files: ["docs"] }));
145
+ fs.writeFileSync(
146
+ path.join(dir, "package.json"),
147
+ JSON.stringify({ name: "@tryinget/pi-agent-vent", version: "0.2.0", files: ["docs"] }),
148
+ );
118
149
  fs.writeFileSync(path.join(dir, "README.md"), "[guide](docs/guide.md)\n");
119
150
  fs.writeFileSync(path.join(dir, "LICENSE"), "local test license\n");
120
151
  fs.mkdirSync(path.join(dir, "docs"));
121
152
  fs.writeFileSync(path.join(dir, "docs", "guide.md"), "# Guide\n");
122
153
  const packJsonText = JSON.stringify([
123
154
  {
155
+ id: "@tryinget/pi-agent-vent@0.2.0",
156
+ name: "@tryinget/pi-agent-vent",
157
+ version: "0.2.0",
124
158
  files: [
125
159
  { path: "README.md" },
126
160
  { path: "LICENSE" },
@@ -142,18 +176,51 @@ test("release artifact check accepts directory files entries and always-included
142
176
  }
143
177
  });
144
178
 
179
+ test("release artifact check rejects pack identity that differs from the local manifest", () => {
180
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-vent-release-identity-"));
181
+ try {
182
+ fs.writeFileSync(
183
+ path.join(dir, "package.json"),
184
+ JSON.stringify({ name: "@tryinget/pi-agent-vent", version: "0.2.0", files: ["README.md"] }),
185
+ );
186
+ fs.writeFileSync(path.join(dir, "README.md"), "# Agent Vent\n");
187
+ const packJsonText = JSON.stringify([
188
+ {
189
+ id: "@tryinget/not-pi-agent-vent@999.0.0",
190
+ name: "@tryinget/not-pi-agent-vent",
191
+ version: "999.0.0",
192
+ files: [{ path: "README.md" }, { path: "package.json" }],
193
+ },
194
+ ]);
195
+
196
+ assert.throws(
197
+ () => runReleaseArtifactCheck({ cwd: dir, packJsonText, log: () => {} }),
198
+ /npm pack identity mismatch/,
199
+ );
200
+ } finally {
201
+ fs.rmSync(dir, { recursive: true, force: true });
202
+ }
203
+ });
204
+
145
205
  test("release artifact check reports packaged markdown failures with actionable wording", () => {
146
206
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-vent-release-check-"));
147
207
  try {
148
208
  fs.writeFileSync(
149
209
  path.join(dir, "package.json"),
150
- JSON.stringify({ files: ["README.md", "docs/guide.md"] }),
210
+ JSON.stringify({
211
+ name: "@tryinget/pi-agent-vent",
212
+ version: "0.2.0",
213
+ files: ["README.md", "docs/guide.md"],
214
+ }),
151
215
  );
152
216
  fs.mkdirSync(path.join(dir, "docs"));
153
217
  fs.writeFileSync(path.join(dir, "README.md"), "[missing](docs/missing.md)\n");
154
218
  fs.writeFileSync(path.join(dir, "docs", "guide.md"), "# Guide\n");
155
219
  const packJsonText = JSON.stringify([
156
220
  {
221
+ id: "@tryinget/pi-agent-vent@0.2.0",
222
+ name: "@tryinget/pi-agent-vent",
223
+ version: "0.2.0",
157
224
  files: [{ path: "README.md" }, { path: "docs/guide.md" }, { path: "package.json" }],
158
225
  },
159
226
  ]);
@@ -1,3 +1,9 @@
1
+ // ---
2
+ // summary: tests isolated release-smoke contracts for installed agent_vent artifacts
3
+ // read_when:
4
+ // - changing tarball source validation, package discovery, or path-tool smoke probes
5
+ // ---
6
+
1
7
  import assert from "node:assert/strict";
2
8
  import fs from "node:fs";
3
9
  import os from "node:os";
@@ -17,17 +23,17 @@ import {
17
23
  test("release smoke release-age bypass is gated by one exact host contract", () => {
18
24
  const packageJson = {
19
25
  devDependencies: {
20
- "@earendil-works/pi-ai": "0.80.6",
21
- "@earendil-works/pi-coding-agent": "0.80.6",
26
+ "@earendil-works/pi-ai": "0.83.0",
27
+ "@earendil-works/pi-coding-agent": "0.83.0",
22
28
  },
23
29
  };
24
- assert.equal(assertExactHostContract({ packageJson, hostVersion: "0.80.6" }), "0.80.6");
30
+ assert.equal(assertExactHostContract({ packageJson, hostVersion: "0.83.0" }), "0.83.0");
25
31
  assert.throws(
26
- () => assertExactHostContract({ packageJson, hostVersion: "^0.80.6" }),
32
+ () => assertExactHostContract({ packageJson, hostVersion: "^0.83.0" }),
27
33
  /requires an exact Pi host version/,
28
34
  );
29
35
  assert.throws(
30
- () => assertExactHostContract({ packageJson, hostVersion: "0.80.7" }),
36
+ () => assertExactHostContract({ packageJson, hostVersion: "0.83.1" }),
31
37
  /host contract mismatch/,
32
38
  );
33
39
  });
@@ -1,3 +1,9 @@
1
+ // ---
2
+ // summary: tests agent vent persistence, redaction, review projections, and retention safety
3
+ // read_when:
4
+ // - modifying vent-store records, filters, curation, exports, backups, or filesystem guards
5
+ // ---
6
+
1
7
  import assert from "node:assert/strict";
2
8
  import { createHash } from "node:crypto";
3
9
  import fs from "node:fs";
@@ -121,18 +127,31 @@ test("category aliases normalize at record and review-filter boundaries", () =>
121
127
  summary: "Missing affordance alias should preserve meaning",
122
128
  category: "missing_affordance",
123
129
  });
130
+ const alignment = createVentRecord({
131
+ summary: "Context alignment candidate should remain callable",
132
+ category: "context_alignment",
133
+ });
124
134
 
125
135
  assert.equal(workflow.category, "workflow");
126
136
  assert.equal(workflow.recurrenceKey, "workflow:workflow-alias");
127
137
  assert.equal(missing.category, "missing_capability");
128
138
  assert.match(missing.recurrenceKey, /^missing_capability:/);
139
+ assert.equal(alignment.category, "other");
140
+ assert.match(alignment.recurrenceKey, /^other:/);
129
141
 
130
- const queue = summarizeReviewQueue([workflow, missing], [], {
142
+ const queue = summarizeReviewQueue([workflow, missing, alignment], [], {
131
143
  filters: { category: "workflow_friction" },
132
144
  });
133
145
  assert.equal(queue.filters.category, "workflow");
134
146
  assert.equal(queue.matchingGroupCount, 1);
135
147
  assert.equal(queue.items[0].recurrenceKey, workflow.recurrenceKey);
148
+
149
+ const alignmentQueue = summarizeReviewQueue([workflow, missing, alignment], [], {
150
+ filters: { category: "context_alignment" },
151
+ });
152
+ assert.equal(alignmentQueue.filters.category, "other");
153
+ assert.equal(alignmentQueue.matchingGroupCount, 1);
154
+ assert.equal(alignmentQueue.items[0].recurrenceKey, alignment.recurrenceKey);
136
155
  });
137
156
 
138
157
  test("explicit recurrence keys are redacted before slugging", () => {