@webpresso/agent-kit 3.1.26 → 3.1.28

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 (62) hide show
  1. package/catalog/AGENTS.md.tpl +6 -10
  2. package/catalog/agent/commands/verify.md +2 -2
  3. package/catalog/agent/rules/changeset-release.md +4 -2
  4. package/catalog/agent/rules/workflow-skills-routing.md +6 -3
  5. package/catalog/agent/skills/autopilot/SKILL.md +48 -11
  6. package/catalog/agent/skills/claude/SKILL.md +7 -0
  7. package/catalog/agent/skills/codex/SKILL.md +7 -0
  8. package/catalog/agent/skills/fix/SKILL.md +33 -8
  9. package/catalog/agent/skills/grok/SKILL.md +7 -0
  10. package/catalog/agent/skills/investigate/SKILL.md +12 -1
  11. package/catalog/agent/skills/pll/SKILL.md +53 -48
  12. package/catalog/agent/skills/team/SKILL.md +26 -9
  13. package/catalog/agent/skills/ultragoal/SKILL.md +147 -19
  14. package/catalog/agent/skills/verify/SKILL.md +71 -51
  15. package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +8 -0
  16. package/dist/esm/audit/blueprint-lifecycle-sql.js +25 -1
  17. package/dist/esm/ci/native-session-memory-cache.d.ts +9 -1
  18. package/dist/esm/ci/native-session-memory-cache.js +9 -1
  19. package/dist/esm/ci/vitest-ci-shards.d.ts +6 -4
  20. package/dist/esm/ci/vitest-ci-shards.js +6 -4
  21. package/dist/esm/cli/auto-update/guard-tombstone.d.ts +25 -0
  22. package/dist/esm/cli/auto-update/guard-tombstone.js +67 -0
  23. package/dist/esm/cli/auto-update/installer.d.ts +4 -17
  24. package/dist/esm/cli/auto-update/installer.js +6 -57
  25. package/dist/esm/cli/auto-update/self-invocation.d.ts +37 -0
  26. package/dist/esm/cli/auto-update/self-invocation.js +62 -0
  27. package/dist/esm/cli/commands/audit.js +1 -0
  28. package/dist/esm/cli/commands/dash/index.js +3 -3
  29. package/dist/esm/cli/commands/dash/plan-once-attention.d.ts +32 -0
  30. package/dist/esm/cli/commands/dash/plan-once-attention.js +50 -0
  31. package/dist/esm/cli/commands/dash/plan-once-schema.d.ts +73 -0
  32. package/dist/esm/cli/commands/dash/plan-once-schema.js +119 -0
  33. package/dist/esm/cli/commands/dash/plan-once.d.ts +122 -0
  34. package/dist/esm/cli/commands/dash/plan-once.js +224 -0
  35. package/dist/esm/cli/commands/dash/tui/ipc-protocol.d.ts +87 -0
  36. package/dist/esm/cli/commands/dash/tui/ipc-protocol.js +128 -0
  37. package/dist/esm/cli/commands/dash/tui/native-host.d.ts +34 -0
  38. package/dist/esm/cli/commands/dash/tui/native-host.js +256 -0
  39. package/dist/esm/cli/commands/dash/tui/native-path.d.ts +11 -0
  40. package/dist/esm/cli/commands/dash/tui/native-path.js +41 -0
  41. package/dist/esm/cli/commands/dash/tui/runtime.js +72 -0
  42. package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +1 -1
  43. package/dist/esm/cli/commands/package-manager.js +2 -1
  44. package/dist/esm/cli/commands/self-install-guard.d.ts +14 -32
  45. package/dist/esm/cli/commands/self-install-guard.js +17 -63
  46. package/dist/esm/hooks/guard-switch/index.js +8 -4
  47. package/dist/esm/hooks/post-tool/lint-after-edit.js +6 -4
  48. package/dist/esm/hooks/precompact/index.js +5 -3
  49. package/dist/esm/hooks/sessionstart/index.d.ts +10 -0
  50. package/dist/esm/hooks/sessionstart/index.js +61 -4
  51. package/dist/esm/hooks/shared/types.d.ts +11 -0
  52. package/dist/esm/hooks/shared/types.js +21 -0
  53. package/dist/esm/paths/state-root.js +6 -0
  54. package/dist/esm/session-memory/current-session.js +2 -0
  55. package/dist/esm/session-memory/native-runtime.js +5 -3
  56. package/dist/esm/session-memory/native-warm-markers.d.ts +18 -0
  57. package/dist/esm/session-memory/native-warm-markers.js +22 -0
  58. package/dist/esm/session-memory/sync/types.d.ts +4 -0
  59. package/dist/esm/session-memory/types.d.ts +3 -1
  60. package/dist/esm/session-memory/types.js +2 -0
  61. package/dist/esm/ultragoal/runtime.js +44 -2
  62. package/package.json +15 -12
@@ -5,11 +5,11 @@ import { tmpdir } from "node:os";
5
5
  import { runHook } from "#hooks/shared/hook-bootstrap";
6
6
  import { resolveHookProjectDir } from "#hooks/shared/project-dir.js";
7
7
  import { createManagedHookExecutionContext, reportSessionMemoryHookDegradation, sessionMemoryHookStoreOptions, } from "#hooks/shared/session-memory-deadline.js";
8
- import { getCommand, getContent, getFilePath, isBashInput, isFileEditInput, isFileReadInput, isFileWriteInput, } from "#hooks/shared/types";
8
+ import { getCommand, getContent, getFilePath, isBashInput, isFileEditInput, isFileReadInput, isFileWriteInput, resolveHookSessionId, } from "#hooks/shared/types";
9
9
  import { isDirectEntrypoint } from "#hooks/shared/direct-entrypoint";
10
10
  import { getSurfacePath, NotInGitRepoError } from "#paths/state-root.js";
11
11
  import { buildContinuityEvent } from "#session-memory/hook-capture.js";
12
- import { repoHashFromRoot } from "#session-memory/repo-hash.js";
12
+ import { computeRepoHash, repoHashFromRoot } from "#session-memory/repo-hash.js";
13
13
  import { SessionMemorySessionStore, } from "#session-memory/session.js";
14
14
  export const LINTABLE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".json", ".css"];
15
15
  export const DEFAULT_MAX_CAPTURE_BYTES = 2048;
@@ -116,7 +116,8 @@ export function capturePostToolUse(input, projectDir, env = process.env, deps =
116
116
  const store = deps.createStore?.(dbPath, deps.storeOptions) ??
117
117
  new SessionMemorySessionStore(dbPath, deps.storeOptions);
118
118
  try {
119
- const repoHash = deps.repoHash?.(projectDir) ?? repoHashFromRoot(projectDir);
119
+ // Match readCurrentSessionId hashing (computeRepoHash), not path-only.
120
+ const repoHash = deps.repoHash?.(projectDir) ?? computeRepoHash(projectDir);
120
121
  const event = buildContinuityEvent({
121
122
  eventType,
122
123
  toolName: input.tool_name ?? "PostToolUse",
@@ -131,10 +132,11 @@ export function capturePostToolUse(input, projectDir, env = process.env, deps =
131
132
  },
132
133
  maxContentBytes: DEFAULT_MAX_CAPTURE_BYTES,
133
134
  });
135
+ const sessionId = resolveHookSessionId(input, env);
134
136
  store.captureEvent({
135
137
  repoHash,
136
138
  agentId: input.tool_name ?? "default",
137
- ...(input.session_id === undefined ? {} : { sessionId: input.session_id }),
139
+ ...(sessionId === undefined ? {} : { sessionId }),
138
140
  event: { ...event, ts: (deps.now?.() ?? new Date()).toISOString() },
139
141
  });
140
142
  return true;
@@ -6,9 +6,10 @@ import { isDirectEntrypoint } from "#hooks/shared/direct-entrypoint";
6
6
  import { runHook } from "#hooks/shared/hook-bootstrap";
7
7
  import { resolveHookProjectDir } from "#hooks/shared/project-dir.js";
8
8
  import { createManagedHookExecutionContext, reportSessionMemoryHookDegradation, sessionMemoryHookStoreOptions, } from "#hooks/shared/session-memory-deadline.js";
9
+ import { resolveHookSessionId } from "#hooks/shared/types.js";
9
10
  import { getSurfacePath, NotInGitRepoError } from "#paths/state-root.js";
10
11
  import { buildContinuityEvent } from "#session-memory/hook-capture.js";
11
- import { repoHashFromRoot } from "#session-memory/repo-hash.js";
12
+ import { computeRepoHash, repoHashFromRoot } from "#session-memory/repo-hash.js";
12
13
  import { SessionMemorySessionStore, } from "#session-memory/session.js";
13
14
  export const DEFAULT_MAX_SNAPSHOT_BYTES = 32 * 1024;
14
15
  export const DEFAULT_MAX_EVENT_BYTES = 2 * 1024;
@@ -61,14 +62,15 @@ export function buildOutput(inputValue, cwd, env, deps = {}) {
61
62
  try {
62
63
  const input = normalizeInput(inputValue);
63
64
  const projectDir = resolveHookProjectDir(input, cwd, env);
64
- const repoHash = deps.repoHash?.(projectDir) ?? repoHashFromRoot(projectDir);
65
+ // Match readCurrentSessionId hashing (computeRepoHash), not path-only.
66
+ const repoHash = deps.repoHash?.(projectDir) ?? computeRepoHash(projectDir);
65
67
  const dbPath = deps.dbPath ?? resolveSessionMemoryDbPath(projectDir, env);
66
68
  mkdirSync(dirname(dbPath), { recursive: true });
67
69
  const store = deps.createStore?.(dbPath, deps.storeOptions) ??
68
70
  new SessionMemorySessionStore(dbPath, deps.storeOptions);
69
71
  try {
70
72
  const agentId = input.agent_id ?? input.agent_type ?? "default";
71
- const sessionId = input.session_id;
73
+ const sessionId = resolveHookSessionId(input, env);
72
74
  const timestamp = (deps.now?.() ?? new Date()).toISOString();
73
75
  const boundary = buildContinuityEvent({
74
76
  eventType: "compaction_boundary",
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import { Database } from "#db/sqlite.js";
3
+ import { SessionMemorySessionStore } from "#session-memory/session.js";
3
4
  export declare const MAX_BYTES: number;
4
5
  export declare const TRUNCATION_NOTICE = "\n\n[truncated: file exceeded 200KB limit]";
5
6
  export declare const RESUME_MAX_EVENT_BYTES: number;
@@ -10,10 +11,19 @@ type StartInput = Record<string, unknown>;
10
11
  type EnvLike = Record<string, string | undefined>;
11
12
  export interface SessionStartDeps {
12
13
  readonly createDatabase?: (dbPath: string) => Pick<Database, "prepare" | "close">;
14
+ readonly createStore?: (dbPath: string) => Pick<SessionMemorySessionStore, "captureEvent" | "close">;
13
15
  readonly dbPath?: string;
14
16
  readonly repoHash?: (projectDir: string) => string;
17
+ readonly now?: () => Date;
18
+ readonly onStorageError?: (error: unknown) => void;
15
19
  }
16
20
  export declare function resolveSessionMemoryDbPath(projectDir: string, env: EnvLike): string;
21
+ /**
22
+ * Seed session-memory with a real host session id so `wp session-info` works
23
+ * under Grok (and similar hosts) before any Edit/Write tool capture. Fail-open:
24
+ * storage errors never break SessionStart stdout.
25
+ */
26
+ export declare function seedSessionIdentityCapture(input: StartInput, projectDir: string, env: EnvLike, deps?: SessionStartDeps): void;
17
27
  /**
18
28
  * Pure function: given a parsed input payload, a working directory, and
19
29
  * environment variables, produce the JSON string that the hook should write
@@ -18,20 +18,23 @@
18
18
  * Always emits valid JSON; `additionalContext` is empty when there is no
19
19
  * `.agent/routing.md`, continuity, or update banner to surface.
20
20
  */
21
- import { readFileSync, statSync } from "node:fs";
21
+ import { mkdirSync, readFileSync, statSync } from "node:fs";
22
22
  import { tmpdir } from "node:os";
23
23
  import { performance } from "node:perf_hooks";
24
- import { join } from "node:path";
24
+ import { dirname, join } from "node:path";
25
25
  import { renderSessionStartInstructionContext } from "#hooks/shared/instruction-surfaces";
26
26
  import { writeHookOutput } from "#hooks/shared/hook-bootstrap";
27
27
  import { rememberHookStdin } from "#hooks/logs/store.js";
28
28
  import { resolveHookProjectDir } from "#hooks/shared/project-dir.js";
29
+ import { resolveHookSessionId } from "#hooks/shared/types.js";
29
30
  import { getSurfacePath, NotInGitRepoError } from "#paths/state-root.js";
30
31
  import { WP_AGENT_LAUNCHED_VIA_WP_ENV, WP_AGENT_LAUNCHED_VIA_WP_VALUE, } from "#runtime/agent-launch-marker.js";
31
32
  import { Database } from "#db/sqlite.js";
32
33
  import { readUpdateBanner } from "./update-banner.js";
33
34
  import { isDirectEntrypoint } from "#hooks/shared/direct-entrypoint";
34
- import { repoHashFromRoot } from "#session-memory/repo-hash.js";
35
+ import { buildContinuityEvent } from "#session-memory/hook-capture.js";
36
+ import { computeRepoHash, repoHashFromRoot } from "#session-memory/repo-hash.js";
37
+ import { SessionMemorySessionStore } from "#session-memory/session.js";
35
38
  export const MAX_BYTES = 200 * 1024;
36
39
  export const TRUNCATION_NOTICE = "\n\n[truncated: file exceeded 200KB limit]";
37
40
  export const RESUME_MAX_EVENT_BYTES = 2 * 1024;
@@ -223,7 +226,8 @@ function buildResumeContext(input, projectDir, env, deps) {
223
226
  const rows = readRecentContinuityEvents({
224
227
  db,
225
228
  repoHash,
226
- sessionId: optionalString(input["session_id"]),
229
+ // Prefer stdin session id; fall back to GROK_SESSION_ID for Grok hooks.
230
+ sessionId: resolveHookSessionId(input, env),
227
231
  minPriority: parsePositiveInt(env.WP_SESSIONSTART_RESUME_MIN_PRIORITY, RESUME_MIN_PRIORITY),
228
232
  limit: parsePositiveInt(env.WP_SESSIONSTART_RESUME_MAX_EVENTS, 16),
229
233
  });
@@ -267,6 +271,56 @@ function buildDirectLaunchWarning(input, env) {
267
271
  "</wp_launch_warning>",
268
272
  ].join("\n");
269
273
  }
274
+ /**
275
+ * Seed session-memory with a real host session id so `wp session-info` works
276
+ * under Grok (and similar hosts) before any Edit/Write tool capture. Fail-open:
277
+ * storage errors never break SessionStart stdout.
278
+ */
279
+ export function seedSessionIdentityCapture(input, projectDir, env, deps = {}) {
280
+ const sessionId = resolveHookSessionId(input, env);
281
+ if (!sessionId)
282
+ return;
283
+ try {
284
+ const dbPath = deps.dbPath ?? resolveSessionMemoryDbPath(projectDir, env);
285
+ mkdirSync(dirname(dbPath), { recursive: true });
286
+ const store = deps.createStore?.(dbPath) ?? new SessionMemorySessionStore(dbPath);
287
+ try {
288
+ // Match `readCurrentSessionId` (computeRepoHash), not path-only hashing.
289
+ const repoHash = deps.repoHash?.(projectDir) ?? computeRepoHash(projectDir);
290
+ const event = buildContinuityEvent({
291
+ eventType: "session_identity",
292
+ toolName: "SessionStart",
293
+ content: `Session identity: ${sessionId}`,
294
+ summary: "Session identity for PR disclosure",
295
+ // Below RESUME_MIN_PRIORITY (50): kept for `wp session-info`, not resume inject.
296
+ priority: 40,
297
+ metadata: {
298
+ source: "sessionstart-hook",
299
+ hookEventName: "SessionStart",
300
+ sessionStartSource: sessionStartSource(input),
301
+ },
302
+ });
303
+ store.captureEvent({
304
+ repoHash,
305
+ agentId: "SessionStart",
306
+ sessionId,
307
+ event: {
308
+ ...event,
309
+ ts: (deps.now?.() ?? new Date()).toISOString(),
310
+ },
311
+ });
312
+ }
313
+ finally {
314
+ store.close();
315
+ }
316
+ }
317
+ catch (error) {
318
+ deps.onStorageError?.(error);
319
+ if (env.WP_SESSIONSTART_DEBUG === "1") {
320
+ process.stderr.write(`wp-sessionstart-identity: ${error.message}\n`);
321
+ }
322
+ }
323
+ }
270
324
  /**
271
325
  * Pure function: given a parsed input payload, a working directory, and
272
326
  * environment variables, produce the JSON string that the hook should write
@@ -277,6 +331,9 @@ function buildDirectLaunchWarning(input, env) {
277
331
  */
278
332
  export function buildOutput(input, cwd, env, deps = {}) {
279
333
  const projectDir = resolveHookProjectDir(input, cwd, env);
334
+ // Capture before resume read so this SessionStart can seed disclosure even
335
+ // when no prior events exist (Grok option-3 path).
336
+ seedSessionIdentityCapture(input, projectDir, env, deps);
280
337
  const target = join(projectDir, ".agent", "routing.md");
281
338
  let routingMd = null;
282
339
  try {
@@ -29,6 +29,16 @@ export type UpdatedInputEnvelope = {
29
29
  */
30
30
  export declare function normalizeHookToolInput(raw: Record<string, unknown>): ToolInput;
31
31
  export declare function parseToolInput(json: string): ToolInput;
32
+ type EnvLike = Record<string, string | undefined>;
33
+ /**
34
+ * Resolve a real host session id for session-memory capture / PR disclosure.
35
+ * Prefer stdin session fields (snake_case Claude/Codex or camelCase Grok), then
36
+ * Grok hook env `GROK_SESSION_ID` (documented for every Grok hook process).
37
+ */
38
+ export declare function resolveHookSessionId(input: {
39
+ readonly session_id?: unknown;
40
+ readonly sessionId?: unknown;
41
+ }, env?: EnvLike): string | undefined;
32
42
  export declare function isBashInput(input: ToolInput): boolean;
33
43
  export declare function isFileEditInput(input: ToolInput): boolean;
34
44
  export declare function isFileWriteInput(input: ToolInput): boolean;
@@ -72,3 +82,4 @@ export type DenyEnvelope = ClaudeCodexDenyEnvelope | GrokDualDenyEnvelope;
72
82
  export declare function buildDenyEnvelope(options: {
73
83
  readonly reason: string;
74
84
  }, env?: NodeJS.ProcessEnv): DenyEnvelope;
85
+ export {};
@@ -55,6 +55,27 @@ export function parseToolInput(json) {
55
55
  }
56
56
  return normalizeHookToolInput(parsed);
57
57
  }
58
+ /**
59
+ * Resolve a real host session id for session-memory capture / PR disclosure.
60
+ * Prefer stdin session fields (snake_case Claude/Codex or camelCase Grok), then
61
+ * Grok hook env `GROK_SESSION_ID` (documented for every Grok hook process).
62
+ */
63
+ export function resolveHookSessionId(input, env = process.env) {
64
+ const pick = (value) => {
65
+ if (typeof value !== "string")
66
+ return undefined;
67
+ const trimmed = value.trim();
68
+ return trimmed.length > 0 ? trimmed : undefined;
69
+ };
70
+ // Prefer snake_case when both present (Claude/Codex contract).
71
+ const fromInput = pick(input.session_id) ?? pick(input.sessionId);
72
+ if (fromInput)
73
+ return fromInput;
74
+ const fromEnv = env.GROK_SESSION_ID?.trim();
75
+ if (fromEnv)
76
+ return fromEnv;
77
+ return undefined;
78
+ }
58
79
  export function isBashInput(input) {
59
80
  return "command" in (input.tool_input || {});
60
81
  }
@@ -72,6 +72,12 @@ function sha256Hex(value) {
72
72
  * Probe whether a directory can hold state files.
73
73
  * Used so quality CLIs (wp audit/test/…) keep working in sandboxes that
74
74
  * deny writes under Application Support (e.g. Codex outside-voice).
75
+ *
76
+ * Any probe failure means the root is not usable (permission, ENOSPC, bad
77
+ * path, …). That is intentionally broader than `createCliLogSink`, which only
78
+ * remaps EPERM/EACCES/EROFS so unexpected IO errors stay loud at the log layer.
79
+ * A failed preferred env-paths root falls back once and is memoized for the
80
+ * process; set `WP_STATE_ROOT` to pin an explicit location instead.
75
81
  */
76
82
  function canWriteStateRoot(root) {
77
83
  try {
@@ -23,6 +23,8 @@ const LIVE_SESSION_ENV_KEYS = [
23
23
  "CLAUDE_SESSION_ID",
24
24
  "CLAUDE_CODE_SESSION_ID",
25
25
  "OPENCODE_SESSION_ID",
26
+ // Grok Build: hook processes always get this; tool children may in future builds.
27
+ "GROK_SESSION_ID",
26
28
  "RUNTIME_SESSION_ID",
27
29
  ];
28
30
  export function readLiveSessionIdFromEnv(env) {
@@ -9,6 +9,7 @@ import envPaths from "env-paths";
9
9
  import lockfile from "proper-lockfile";
10
10
  import { resolveAgentKitPackageRoot } from "#cli/commands/init/package-root.js";
11
11
  import { SESSION_MEMORY_NATIVE_ADDON_FILENAME, resolveSessionMemoryNativeTarget, } from "./native-targets.js";
12
+ import { NATIVE_WARM_LOG_CORRUPT_REBUILD, NATIVE_WARM_LOG_HIT, NATIVE_WARM_LOG_REBUILD, } from "./native-warm-markers.js";
12
13
  const requireFromHere = createRequire(import.meta.url);
13
14
  const NATIVE_WORKSPACE_DIRNAME = join("native", "session-memory-engine");
14
15
  const BUILD_PACKAGE = "session-memory-napi";
@@ -240,12 +241,12 @@ function ensureNativeModuleBuilt(options = {}) {
240
241
  const nodePath = compiledNodePath();
241
242
  const force = options.force === true;
242
243
  if (!force && !shouldRebuildNativeSessionMemoryNode(nodePath, workspaceRoot)) {
243
- console.log(`[native-session-memory] warm=hit path=${nodePath} trust=${trustCachedNativeNode() ? "1" : "0"}`);
244
+ console.log(`[native-session-memory] ${NATIVE_WARM_LOG_HIT}${nodePath} trust=${trustCachedNativeNode() ? "1" : "0"}`);
244
245
  return nodePath;
245
246
  }
246
247
  return withNativeBuildLock(sessionMemoryNativeCacheRoot(), () => {
247
248
  if (!force && !shouldRebuildNativeSessionMemoryNode(nodePath, workspaceRoot)) {
248
- console.log(`[native-session-memory] warm=hit path=${nodePath} trust=${trustCachedNativeNode() ? "1" : "0"}`);
249
+ console.log(`[native-session-memory] ${NATIVE_WARM_LOG_HIT}${nodePath} trust=${trustCachedNativeNode() ? "1" : "0"}`);
249
250
  return nodePath;
250
251
  }
251
252
  if (force && existsSync(nodePath)) {
@@ -256,7 +257,8 @@ function ensureNativeModuleBuilt(options = {}) {
256
257
  // Best-effort: cargo rebuild overwrites via copyFileSync below.
257
258
  }
258
259
  }
259
- console.log(`[native-session-memory] warm=${force ? "corrupt-rebuild" : "rebuild"} path=${nodePath} trust=${trustCachedNativeNode() ? "1" : "0"}`);
260
+ const warmMarker = force ? NATIVE_WARM_LOG_CORRUPT_REBUILD : NATIVE_WARM_LOG_REBUILD;
261
+ console.log(`[native-session-memory] ${warmMarker}${nodePath} trust=${trustCachedNativeNode() ? "1" : "0"}`);
260
262
  const targetDir = cargoTargetDir();
261
263
  mkdirSync(targetDir, { recursive: true });
262
264
  try {
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Grep-stable warm-path log markers for the session-memory native warm step.
3
+ *
4
+ * Emitted by `native-runtime` and classified by the Test job E1 summary greps
5
+ * in `.github/workflows/ci.agent-kit.yml`. YAML cannot import TS — contract
6
+ * tests in `src/ci/native-session-memory-cache.test.ts` pin that the workflow
7
+ * greps these exact substrings.
8
+ *
9
+ * Rename only with a coordinated workflow + contract-test update.
10
+ */
11
+ /** Exact-hit warm log marker (`grep -q` target + runtime emit prefix). */
12
+ export declare const NATIVE_WARM_LOG_HIT = "warm=hit path=";
13
+ /** Source/mtime rebuild warm log marker. */
14
+ export declare const NATIVE_WARM_LOG_REBUILD = "warm=rebuild path=";
15
+ /** Forced corrupt-node rebuild warm log marker. */
16
+ export declare const NATIVE_WARM_LOG_CORRUPT_REBUILD = "warm=corrupt-rebuild path=";
17
+ /** All E1-classified warm markers (corrupt-rebuild → hit → rebuild order matches workflow). */
18
+ export declare const NATIVE_WARM_LOG_MARKERS: readonly ["warm=corrupt-rebuild path=", "warm=hit path=", "warm=rebuild path="];
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Grep-stable warm-path log markers for the session-memory native warm step.
3
+ *
4
+ * Emitted by `native-runtime` and classified by the Test job E1 summary greps
5
+ * in `.github/workflows/ci.agent-kit.yml`. YAML cannot import TS — contract
6
+ * tests in `src/ci/native-session-memory-cache.test.ts` pin that the workflow
7
+ * greps these exact substrings.
8
+ *
9
+ * Rename only with a coordinated workflow + contract-test update.
10
+ */
11
+ /** Exact-hit warm log marker (`grep -q` target + runtime emit prefix). */
12
+ export const NATIVE_WARM_LOG_HIT = "warm=hit path=";
13
+ /** Source/mtime rebuild warm log marker. */
14
+ export const NATIVE_WARM_LOG_REBUILD = "warm=rebuild path=";
15
+ /** Forced corrupt-node rebuild warm log marker. */
16
+ export const NATIVE_WARM_LOG_CORRUPT_REBUILD = "warm=corrupt-rebuild path=";
17
+ /** All E1-classified warm markers (corrupt-rebuild → hit → rebuild order matches workflow). */
18
+ export const NATIVE_WARM_LOG_MARKERS = [
19
+ NATIVE_WARM_LOG_CORRUPT_REBUILD,
20
+ NATIVE_WARM_LOG_HIT,
21
+ NATIVE_WARM_LOG_REBUILD,
22
+ ];
@@ -48,6 +48,7 @@ export declare const shareContinuityEventV1: z.ZodObject<{
48
48
  helper_waiting: "helper_waiting";
49
49
  rejected_approach: "rejected_approach";
50
50
  rule_snapshot: "rule_snapshot";
51
+ session_identity: "session_identity";
51
52
  tool_command: "tool_command";
52
53
  tool_edit: "tool_edit";
53
54
  tool_read: "tool_read";
@@ -81,6 +82,7 @@ export declare const shareAgentRunDeltaV1: z.ZodObject<{
81
82
  helper_waiting: "helper_waiting";
82
83
  rejected_approach: "rejected_approach";
83
84
  rule_snapshot: "rule_snapshot";
85
+ session_identity: "session_identity";
84
86
  tool_command: "tool_command";
85
87
  tool_edit: "tool_edit";
86
88
  tool_read: "tool_read";
@@ -119,6 +121,7 @@ export declare const localAgentRunDeltaEnvelopeV1: z.ZodObject<{
119
121
  helper_waiting: "helper_waiting";
120
122
  rejected_approach: "rejected_approach";
121
123
  rule_snapshot: "rule_snapshot";
124
+ session_identity: "session_identity";
122
125
  tool_command: "tool_command";
123
126
  tool_edit: "tool_edit";
124
127
  tool_read: "tool_read";
@@ -155,6 +158,7 @@ export declare const sessionShareEnvelopeV1: z.ZodObject<{
155
158
  helper_waiting: "helper_waiting";
156
159
  rejected_approach: "rejected_approach";
157
160
  rule_snapshot: "rule_snapshot";
161
+ session_identity: "session_identity";
158
162
  tool_command: "tool_command";
159
163
  tool_edit: "tool_edit";
160
164
  tool_read: "tool_read";
@@ -23,7 +23,9 @@ export interface SessionMemorySearchResult extends IndexedSessionMemoryChunk {
23
23
  score: number;
24
24
  tier: "porter" | "trigram" | "levenshtein";
25
25
  }
26
- export declare const SESSION_CONTINUITY_EVENT_TYPES: readonly ["user_prompt", "decision", "constraint", "tool_read", "tool_edit", "tool_command", "failure", "rejected_approach", "assistant_turn_summary", "compaction_boundary", "rule_snapshot", "helper_selected", "helper_started", "helper_waiting", "helper_completed", "helper_failed", "helper_returned"];
26
+ export declare const SESSION_CONTINUITY_EVENT_TYPES: readonly ["user_prompt", "decision", "constraint", "tool_read", "tool_edit", "tool_command", "failure", "rejected_approach", "assistant_turn_summary", "compaction_boundary", "rule_snapshot",
27
+ /** SessionStart seed so `wp session-info` can resolve a real host session id. */
28
+ "session_identity", "helper_selected", "helper_started", "helper_waiting", "helper_completed", "helper_failed", "helper_returned"];
27
29
  export type SessionContinuityEventType = (typeof SESSION_CONTINUITY_EVENT_TYPES)[number];
28
30
  /**
29
31
  * Optional dashboard identity carried in continuity-event metadata. It keeps
@@ -10,6 +10,8 @@ export const SESSION_CONTINUITY_EVENT_TYPES = [
10
10
  "assistant_turn_summary",
11
11
  "compaction_boundary",
12
12
  "rule_snapshot",
13
+ /** SessionStart seed so `wp session-info` can resolve a real host session id. */
14
+ "session_identity",
13
15
  "helper_selected",
14
16
  "helper_started",
15
17
  "helper_waiting",
@@ -2,8 +2,9 @@ import { spawnSync } from "node:child_process";
2
2
  import { createHash } from "node:crypto";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { runQaCommand } from "#cli/commands/qa.js";
5
- import { validateTrackedReviewProvenance } from "#lifecycle/review-provenance.js";
5
+ import { collectV2ReviewProvenance, validateTrackedReviewProvenance, } from "#lifecycle/review-provenance.js";
6
6
  import { evaluateRequiredChecks } from "#mcp/tools/_shared/required-checks.js";
7
+ import { createDeliverySubjectAtRef, createPlanSubjectAtRef } from "#review/subject.js";
7
8
  import { scanBlueprintDirectory } from "#service/scanner.js";
8
9
  import { resolveBlueprintRoot } from "#utils/blueprint-root.js";
9
10
  import matter from "gray-matter";
@@ -105,6 +106,11 @@ async function collectPrProbe(cwd) {
105
106
  function hasRequiredGreenChecks(checks) {
106
107
  return evaluateRequiredChecks(checks).green;
107
108
  }
109
+ /**
110
+ * Validation needs two distinct reviewers bound to the current ultragoal source
111
+ * snapshot. Prefer legacy frontmatter+reviews.md; fall back to current v2 plan +
112
+ * delivery authorities (distinct lanes) when their subjects still match HEAD.
113
+ */
108
114
  function collectValidationEvidence(cwd, blueprintSlugs, sourceSnapshot) {
109
115
  const entries = scanBlueprintDirectory({
110
116
  baseDir: resolveBlueprintRoot(cwd),
@@ -115,7 +121,43 @@ function collectValidationEvidence(cwd, blueprintSlugs, sourceSnapshot) {
115
121
  if (!entry)
116
122
  return undefined;
117
123
  const approvals = matter(readFileSync(entry.path, "utf8")).data.approvals;
118
- return validateTrackedReviewProvenance(entry.path, approvals, { sourceSnapshot });
124
+ const legacy = validateTrackedReviewProvenance(entry.path, approvals, { sourceSnapshot });
125
+ if (legacy.valid)
126
+ return legacy;
127
+ const stableSlug = slug.includes("/") ? (slug.split("/").pop() ?? slug) : slug;
128
+ let deliveryDigest;
129
+ let planDigest;
130
+ try {
131
+ deliveryDigest = createDeliverySubjectAtRef(cwd, "HEAD", stableSlug).digest;
132
+ planDigest = createPlanSubjectAtRef(cwd, "HEAD", stableSlug).digest;
133
+ }
134
+ catch {
135
+ return undefined;
136
+ }
137
+ const bind = (records) => records.map((record) => ({
138
+ reviewer: record.reviewer,
139
+ artifact: record.artifact,
140
+ sourceSnapshot,
141
+ targetHash: record.targetHash,
142
+ }));
143
+ const delivery = bind(collectV2ReviewProvenance(entry.path, "delivery", "HEAD").filter((record) => record.targetHash === deliveryDigest));
144
+ const plan = bind(collectV2ReviewProvenance(entry.path, "plan", "HEAD").filter((record) => record.targetHash === planDigest));
145
+ // Prefer delivery as code-review lane; plan as verify/architecture lane.
146
+ const byReviewer = new Map();
147
+ for (const record of [...plan, ...delivery]) {
148
+ byReviewer.set(record.reviewer, record);
149
+ }
150
+ const v2Records = [...byReviewer.values()];
151
+ const distinctReviewers = [...new Set(v2Records.map((record) => record.reviewer))];
152
+ const distinctArtifacts = [...new Set(v2Records.map((record) => record.artifact))];
153
+ return {
154
+ distinctArtifacts,
155
+ distinctReviewers,
156
+ records: v2Records,
157
+ valid: v2Records.length >= 2 &&
158
+ distinctReviewers.length === v2Records.length &&
159
+ distinctArtifacts.length === v2Records.length,
160
+ };
119
161
  });
120
162
  if (evaluations.some((evaluation) => evaluation?.valid !== true))
121
163
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpresso/agent-kit",
3
- "version": "3.1.26",
3
+ "version": "3.1.28",
4
4
  "private": false,
5
5
  "description": "TypeScript-first agent harness for guarded develop/deploy workflows: wp CLI gates, MCP tools, hooks, memory, worktrees, secrets, audits, and evidence checks.",
6
6
  "keywords": [
@@ -399,6 +399,9 @@
399
399
  "prepare": "husky",
400
400
  "mutation": "./bin/wp test --mutation",
401
401
  "e2e": "./bin/wp e2e --config playwright.config.ts",
402
+ "native:dash-tui:build": "cargo build --manifest-path native/dash-tui/Cargo.toml --release",
403
+ "native:dash-tui:test": "cargo test --manifest-path native/dash-tui/Cargo.toml",
404
+ "native:dash-tui:smoke": "cargo run --manifest-path native/dash-tui/Cargo.toml --release -- --smoke",
402
405
  "native:session-memory:build": "cargo build --manifest-path native/session-memory-engine/Cargo.toml --package session-memory-napi --release --locked",
403
406
  "native:session-memory:fmt": "cargo fmt --manifest-path native/session-memory-engine/Cargo.toml --all -- --check",
404
407
  "native:session-memory:clippy": "cargo clippy --manifest-path native/session-memory-engine/Cargo.toml --all-targets --all-features -- -D warnings",
@@ -499,16 +502,16 @@
499
502
  "setupWpActionRef": "c2c71a7a4be446fc6858e6b57bf55a11ccfa2d88"
500
503
  },
501
504
  "optionalDependencies": {
502
- "@webpresso/agent-kit-runtime-darwin-arm64": "3.1.26",
503
- "@webpresso/agent-kit-runtime-darwin-x64": "3.1.26",
504
- "@webpresso/agent-kit-runtime-linux-x64": "3.1.26",
505
- "@webpresso/agent-kit-runtime-linux-arm64": "3.1.26",
506
- "@webpresso/agent-kit-runtime-windows-x64": "3.1.26",
507
- "@webpresso/agent-kit-session-memory-darwin-x64": "3.1.26",
508
- "@webpresso/agent-kit-session-memory-darwin-arm64": "3.1.26",
509
- "@webpresso/agent-kit-session-memory-linux-x64": "3.1.26",
510
- "@webpresso/agent-kit-session-memory-linux-arm64": "3.1.26",
511
- "@webpresso/agent-kit-session-memory-win32-x64": "3.1.26",
512
- "@webpresso/agent-kit-session-memory-win32-arm64": "3.1.26"
505
+ "@webpresso/agent-kit-runtime-darwin-arm64": "3.1.28",
506
+ "@webpresso/agent-kit-runtime-darwin-x64": "3.1.28",
507
+ "@webpresso/agent-kit-runtime-linux-x64": "3.1.28",
508
+ "@webpresso/agent-kit-runtime-linux-arm64": "3.1.28",
509
+ "@webpresso/agent-kit-runtime-windows-x64": "3.1.28",
510
+ "@webpresso/agent-kit-session-memory-darwin-x64": "3.1.28",
511
+ "@webpresso/agent-kit-session-memory-darwin-arm64": "3.1.28",
512
+ "@webpresso/agent-kit-session-memory-linux-x64": "3.1.28",
513
+ "@webpresso/agent-kit-session-memory-linux-arm64": "3.1.28",
514
+ "@webpresso/agent-kit-session-memory-win32-x64": "3.1.28",
515
+ "@webpresso/agent-kit-session-memory-win32-arm64": "3.1.28"
513
516
  }
514
517
  }