@webpresso/agent-kit 3.1.26 → 3.1.30

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 (150) hide show
  1. package/catalog/AGENTS.md.tpl +7 -11
  2. package/catalog/agent/commands/verify.md +2 -2
  3. package/catalog/agent/rules/changeset-release.md +4 -2
  4. package/catalog/agent/rules/ci-cost-local-first.md +139 -0
  5. package/catalog/agent/rules/ci-test-perf.md +5 -1
  6. package/catalog/agent/rules/cmd-execution.md +9 -0
  7. package/catalog/agent/rules/pre-implementation.md +2 -1
  8. package/catalog/agent/rules/test-scan-perf.md +80 -0
  9. package/catalog/agent/rules/workflow-skills-routing.md +9 -4
  10. package/catalog/agent/skills/autopilot/SKILL.md +48 -11
  11. package/catalog/agent/skills/claude/SKILL.md +7 -0
  12. package/catalog/agent/skills/codex/SKILL.md +7 -0
  13. package/catalog/agent/skills/fix/SKILL.md +36 -8
  14. package/catalog/agent/skills/grok/SKILL.md +7 -0
  15. package/catalog/agent/skills/investigate/SKILL.md +12 -1
  16. package/catalog/agent/skills/pll/SKILL.md +53 -48
  17. package/catalog/agent/skills/team/SKILL.md +26 -9
  18. package/catalog/agent/skills/ultragoal/SKILL.md +147 -19
  19. package/catalog/agent/skills/verify/SKILL.md +89 -51
  20. package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +8 -0
  21. package/dist/esm/audit/blueprint-lifecycle-sql.js +25 -1
  22. package/dist/esm/audit/blueprint-pr-coverage.js +68 -7
  23. package/dist/esm/audit/registry.d.ts +4 -4
  24. package/dist/esm/audit/registry.js +3 -0
  25. package/dist/esm/audit/roadmap-links.js +6 -0
  26. package/dist/esm/audit/test-scan-perf.d.ts +18 -0
  27. package/dist/esm/audit/test-scan-perf.js +275 -0
  28. package/dist/esm/blueprint/lifecycle/engine.d.ts +9 -0
  29. package/dist/esm/blueprint/lifecycle/engine.js +53 -6
  30. package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
  31. package/dist/esm/blueprint/lifecycle/review-provenance.js +93 -12
  32. package/dist/esm/blueprint/markdown/helpers.js +6 -2
  33. package/dist/esm/ci/act-replay.js +110 -7
  34. package/dist/esm/ci/act-runner.d.ts +4 -0
  35. package/dist/esm/ci/act-runner.js +85 -26
  36. package/dist/esm/ci/act-worktree-git.d.ts +11 -0
  37. package/dist/esm/ci/act-worktree-git.js +219 -0
  38. package/dist/esm/ci/native-session-memory-cache.d.ts +9 -1
  39. package/dist/esm/ci/native-session-memory-cache.js +9 -1
  40. package/dist/esm/ci/vitest-ci-shards.d.ts +6 -4
  41. package/dist/esm/ci/vitest-ci-shards.js +6 -4
  42. package/dist/esm/cli/auto-update/guard-tombstone.d.ts +25 -0
  43. package/dist/esm/cli/auto-update/guard-tombstone.js +67 -0
  44. package/dist/esm/cli/auto-update/installer.d.ts +4 -17
  45. package/dist/esm/cli/auto-update/installer.js +6 -57
  46. package/dist/esm/cli/auto-update/self-invocation.d.ts +37 -0
  47. package/dist/esm/cli/auto-update/self-invocation.js +62 -0
  48. package/dist/esm/cli/commands/audit.js +4 -0
  49. package/dist/esm/cli/commands/blueprint/mutations.js +35 -18
  50. package/dist/esm/cli/commands/blueprint/router.js +27 -2
  51. package/dist/esm/cli/commands/ci.d.ts +8 -1
  52. package/dist/esm/cli/commands/ci.js +80 -21
  53. package/dist/esm/cli/commands/dash/index.js +3 -3
  54. package/dist/esm/cli/commands/dash/keymap.js +1 -0
  55. package/dist/esm/cli/commands/dash/launch-preferences.d.ts +15 -0
  56. package/dist/esm/cli/commands/dash/launch-preferences.js +46 -17
  57. package/dist/esm/cli/commands/dash/plan-once-attention.d.ts +32 -0
  58. package/dist/esm/cli/commands/dash/plan-once-attention.js +50 -0
  59. package/dist/esm/cli/commands/dash/plan-once-label.d.ts +7 -0
  60. package/dist/esm/cli/commands/dash/plan-once-label.js +5 -0
  61. package/dist/esm/cli/commands/dash/plan-once-membership.d.ts +67 -0
  62. package/dist/esm/cli/commands/dash/plan-once-membership.js +151 -0
  63. package/dist/esm/cli/commands/dash/plan-once-merge.d.ts +143 -0
  64. package/dist/esm/cli/commands/dash/plan-once-merge.js +238 -0
  65. package/dist/esm/cli/commands/dash/plan-once-schema.d.ts +73 -0
  66. package/dist/esm/cli/commands/dash/plan-once-schema.js +119 -0
  67. package/dist/esm/cli/commands/dash/plan-once.d.ts +142 -0
  68. package/dist/esm/cli/commands/dash/plan-once.js +261 -0
  69. package/dist/esm/cli/commands/dash/tui/attention-hud.d.ts +9 -0
  70. package/dist/esm/cli/commands/dash/tui/attention-hud.js +9 -0
  71. package/dist/esm/cli/commands/dash/tui/chat-bridge.d.ts +41 -0
  72. package/dist/esm/cli/commands/dash/tui/chat-bridge.js +73 -0
  73. package/dist/esm/cli/commands/dash/tui/chat-mode.d.ts +47 -0
  74. package/dist/esm/cli/commands/dash/tui/chat-mode.js +98 -0
  75. package/dist/esm/cli/commands/dash/tui/frame.d.ts +3 -0
  76. package/dist/esm/cli/commands/dash/tui/frame.js +3 -1
  77. package/dist/esm/cli/commands/dash/tui/ipc-protocol.d.ts +87 -0
  78. package/dist/esm/cli/commands/dash/tui/ipc-protocol.js +128 -0
  79. package/dist/esm/cli/commands/dash/tui/native-host.d.ts +34 -0
  80. package/dist/esm/cli/commands/dash/tui/native-host.js +256 -0
  81. package/dist/esm/cli/commands/dash/tui/native-path.d.ts +11 -0
  82. package/dist/esm/cli/commands/dash/tui/native-path.js +41 -0
  83. package/dist/esm/cli/commands/dash/tui/palette.d.ts +1 -1
  84. package/dist/esm/cli/commands/dash/tui/palette.js +14 -0
  85. package/dist/esm/cli/commands/dash/tui/runtime.js +311 -4
  86. package/dist/esm/cli/commands/dash/tui/state.d.ts +41 -0
  87. package/dist/esm/cli/commands/dash/tui/state.js +257 -4
  88. package/dist/esm/cli/commands/format.js +0 -2
  89. package/dist/esm/cli/commands/init/config.d.ts +0 -1
  90. package/dist/esm/cli/commands/init/config.js +1 -4
  91. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.d.ts +5 -6
  92. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.js +6 -13
  93. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.d.ts +0 -2
  94. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.js +0 -2
  95. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.d.ts +6 -3
  96. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.js +39 -26
  97. package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +1 -1
  98. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +61 -23
  99. package/dist/esm/cli/commands/logs.d.ts +15 -0
  100. package/dist/esm/cli/commands/logs.js +192 -3
  101. package/dist/esm/cli/commands/package-manager.js +2 -1
  102. package/dist/esm/cli/commands/quality-log-store.d.ts +1 -0
  103. package/dist/esm/cli/commands/quality-log-store.js +187 -51
  104. package/dist/esm/cli/commands/review.js +5 -8
  105. package/dist/esm/cli/commands/secrets.d.ts +1 -0
  106. package/dist/esm/cli/commands/secrets.js +4 -1
  107. package/dist/esm/cli/commands/self-install-guard.d.ts +14 -32
  108. package/dist/esm/cli/commands/self-install-guard.js +17 -63
  109. package/dist/esm/cli/direct-provider-launch.js +25 -1
  110. package/dist/esm/hooks/doctor.d.ts +15 -0
  111. package/dist/esm/hooks/doctor.js +56 -0
  112. package/dist/esm/hooks/guard-switch/index.js +8 -4
  113. package/dist/esm/hooks/post-tool/lint-after-edit.js +6 -4
  114. package/dist/esm/hooks/precompact/index.js +5 -3
  115. package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +4 -4
  116. package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.d.ts +0 -1
  117. package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.js +8 -5
  118. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.d.ts +9 -7
  119. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +126 -14
  120. package/dist/esm/hooks/sessionstart/index.d.ts +10 -0
  121. package/dist/esm/hooks/sessionstart/index.js +61 -4
  122. package/dist/esm/hooks/shared/types.d.ts +11 -0
  123. package/dist/esm/hooks/shared/types.js +21 -0
  124. package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
  125. package/dist/esm/mcp/tools/audit.d.ts +2 -1
  126. package/dist/esm/mcp/tools/audit.js +10 -0
  127. package/dist/esm/mcp/tools/audits.d.ts +1 -0
  128. package/dist/esm/mcp/tools/ci-act.js +9 -11
  129. package/dist/esm/paths/state-root.js +9 -0
  130. package/dist/esm/review/authority.js +17 -2
  131. package/dist/esm/review/availability.js +18 -5
  132. package/dist/esm/review/subject.d.ts +15 -0
  133. package/dist/esm/review/subject.js +105 -10
  134. package/dist/esm/runtime/executor.d.ts +1 -0
  135. package/dist/esm/runtime/executor.js +2 -1
  136. package/dist/esm/secret-gate/runner.d.ts +1 -0
  137. package/dist/esm/secret-gate/runner.js +4 -1
  138. package/dist/esm/session-memory/current-session.js +2 -0
  139. package/dist/esm/session-memory/native-runtime.js +5 -3
  140. package/dist/esm/session-memory/native-warm-markers.d.ts +18 -0
  141. package/dist/esm/session-memory/native-warm-markers.js +22 -0
  142. package/dist/esm/session-memory/sync/types.d.ts +4 -0
  143. package/dist/esm/session-memory/types.d.ts +3 -1
  144. package/dist/esm/session-memory/types.js +2 -0
  145. package/dist/esm/ultragoal/runtime.js +44 -2
  146. package/dist/esm/worktrees/current.d.ts +1 -0
  147. package/dist/esm/worktrees/current.js +1 -0
  148. package/dist/esm/worktrees/project-agent-surfaces.d.ts +1 -0
  149. package/dist/esm/worktrees/project-agent-surfaces.js +2 -1
  150. package/package.json +17 -14
@@ -1,3 +1,3 @@
1
- export declare const MCP_AUDIT_KINDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "session-memory-hardcut", "worktree-main-ownership", "sast"];
1
+ export declare const MCP_AUDIT_KINDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "session-memory-hardcut", "worktree-main-ownership", "sast"];
2
2
  export type MCPAuditKind = (typeof MCP_AUDIT_KINDS)[number];
3
3
  export declare function isMCPAuditKind(value: string): value is MCPAuditKind;
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { z } from "zod";
17
17
  import type { ToolDescriptor } from "#mcp/auto-discover";
18
- export declare const KINDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "session-memory-hardcut", "worktree-main-ownership", "sast"];
18
+ export declare const KINDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "session-memory-hardcut", "worktree-main-ownership", "sast"];
19
19
  export declare const inputSchema: z.ZodObject<{
20
20
  kind: z.ZodEnum<{
21
21
  "absolute-path-policy": "absolute-path-policy";
@@ -54,6 +54,7 @@ export declare const inputSchema: z.ZodObject<{
54
54
  "session-memory-hardcut": "session-memory-hardcut";
55
55
  "supported-agent-clis": "supported-agent-clis";
56
56
  "tech-debt": "tech-debt";
57
+ "test-scan-perf": "test-scan-perf";
57
58
  "test-smells": "test-smells";
58
59
  "toolchain-isolation": "toolchain-isolation";
59
60
  "tph-e2e": "tph-e2e";
@@ -464,6 +464,16 @@ export async function dispatchAudit(input) {
464
464
  details: auditResult,
465
465
  };
466
466
  }
467
+ case "test-scan-perf": {
468
+ const { auditTestScanPerf } = await import("#audit/test-scan-perf");
469
+ const auditResult = auditTestScanPerf(input.cwd ?? input.directory ?? process.cwd());
470
+ return {
471
+ passed: auditResult.ok,
472
+ summary: summarizeRepoAudit(kind, auditResult),
473
+ kind,
474
+ details: auditResult,
475
+ };
476
+ }
467
477
  case "hook-surface": {
468
478
  const { auditHookSurface } = await import("#audit/hook-surface");
469
479
  const auditResult = auditHookSurface(input.cwd ?? input.directory);
@@ -50,6 +50,7 @@ declare const inputSchema: z.ZodObject<{
50
50
  "session-memory-hardcut": "session-memory-hardcut";
51
51
  "supported-agent-clis": "supported-agent-clis";
52
52
  "tech-debt": "tech-debt";
53
+ "test-scan-perf": "test-scan-perf";
53
54
  "test-smells": "test-smells";
54
55
  "toolchain-isolation": "toolchain-isolation";
55
56
  "tph-e2e": "tph-e2e";
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
- import { DEFAULT_CI_ACT_TIMEOUT_MS, MAX_CI_ACT_TIMEOUT_MS } from "#cli/commands/ci";
3
- import { buildPublicCiActCommand, preparePublicCiActExecution, resolveCiActExecutionMode, resolveCiActSecretEnvProfile, sanitizePublicCiActArgv, } from "#ci/act-runner.js";
2
+ import { DEFAULT_CI_ACT_TIMEOUT_MS, MAX_CI_ACT_TIMEOUT_MS, SMOKE_CI_ACT_TIMEOUT_MS, } from "#cli/commands/ci";
3
+ import { buildPublicCiActCommand, preparePublicCiActExecution, resolveCiActExecutionMode, resolveCiActSecretEnvProfile, sanitizePublicCiActArgv, shouldForceCiActSecretGate, } from "#ci/act-runner.js";
4
4
  import { isSecretGateRuntimeProfile, runSecretGateCommand } from "#secret-gate/runner.js";
5
5
  import { clipRawOutput, createSummaryOutputSchema, createSummaryResult } from "./_shared/result.js";
6
6
  import { resolveProjectRoot } from "./_shared/project-root.js";
@@ -10,6 +10,7 @@ const inputSchema = z
10
10
  cwd: z.string().optional(),
11
11
  workflowPath: z.string(),
12
12
  job: z.string().optional(),
13
+ matrix: z.array(z.string()).optional().default([]),
13
14
  eventName: z
14
15
  .enum(["pull_request", "push", "workflow_dispatch"])
15
16
  .optional()
@@ -24,13 +25,8 @@ const inputSchema = z
24
25
  }),
25
26
  secretProfile: z.string().optional(),
26
27
  mode: z.enum(["direct", "replay"]).optional().default("direct"),
27
- timeoutMs: z
28
- .number()
29
- .int()
30
- .positive()
31
- .max(MAX_CI_ACT_TIMEOUT_MS)
32
- .optional()
33
- .default(DEFAULT_CI_ACT_TIMEOUT_MS),
28
+ smoke: z.boolean().optional().default(false),
29
+ timeoutMs: z.number().int().positive().max(MAX_CI_ACT_TIMEOUT_MS).optional(),
34
30
  containerArchitecture: z.string().optional(),
35
31
  platformImage: z.string().optional().default("ghcr.io/catthehacker/ubuntu:full-latest"),
36
32
  execute: z.boolean().optional().default(false),
@@ -96,6 +92,7 @@ const tool = {
96
92
  handler: async (raw, extra) => {
97
93
  const input = inputSchema.parse(raw ?? {});
98
94
  const cwd = resolveProjectRoot(input.cwd ? { cwd: input.cwd } : {});
95
+ const timeoutMs = input.timeoutMs ?? (input.smoke ? SMOKE_CI_ACT_TIMEOUT_MS : DEFAULT_CI_ACT_TIMEOUT_MS);
99
96
  if (!input.execute) {
100
97
  return createSummaryResult({
101
98
  passed: true,
@@ -108,13 +105,14 @@ const tool = {
108
105
  const secretProfile = resolveCiActSecretEnvProfile({ ...input, cwd }) ?? "preview";
109
106
  const result = await runSecretGateCommand({
110
107
  cwd,
108
+ commandCwd: prepared.cwd,
111
109
  sink: "act",
112
110
  profile: secretProfile,
113
111
  envProfile: input.envProfile,
114
- forceSecretGate: true,
112
+ forceSecretGate: shouldForceCiActSecretGate(input.envProfile),
115
113
  command: "act",
116
114
  args: prepared.command.actArgs,
117
- timeoutMs: input.timeoutMs,
115
+ timeoutMs,
118
116
  signal: extra?.signal,
119
117
  });
120
118
  const merged = [result.stdout, result.stderr].filter(Boolean).join("\n");
@@ -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 {
@@ -241,6 +247,9 @@ export async function getSurfacePathAsync(name, scope, cwd = resolveCwd()) {
241
247
  export async function withLock(scope, fn, cwd) {
242
248
  const lockTarget = await getSurfacePathAsync(".lock", scope, cwd);
243
249
  mkdirSync(dirname(lockTarget), { recursive: true });
250
+ // Keep retries short so lock-contention probes (review gate contender tests)
251
+ // still fail closed in a few seconds. Dual-writer create loops retry at the
252
+ // call site when they need longer budgets under CI load.
244
253
  const release = await lockfile.lock(lockTarget, {
245
254
  realpath: false,
246
255
  retries: { retries: 5, minTimeout: 50, maxTimeout: 500, factor: 2 },
@@ -9,7 +9,7 @@ import { createHash } from "node:crypto";
9
9
  import { existsSync, lstatSync, readFileSync } from "node:fs";
10
10
  import path from "node:path";
11
11
  import { evaluateReviewEvents, parseReviewEventLog } from "./events.js";
12
- import { createDeliverySubjectAtRef, createPlanSubjectAtRef, resolveReviewCommit, } from "./subject.js";
12
+ import { createDeliverySubjectAtRef, createLegacyPlanSubjectAtRef, createPlanSubjectAtRef, resolveReviewCommit, } from "./subject.js";
13
13
  const GIT_TIMEOUT_MS = 5_000;
14
14
  const GIT_MAX_BUFFER_BYTES = 128 * 1024 * 1024;
15
15
  const REGULAR_MODE_PATTERN = /^100(?:644|755)$/u;
@@ -126,12 +126,27 @@ export function readReviewAuthorityAtRef(cwd, ref, stableSlug, purpose) {
126
126
  const subject = purpose === "plan"
127
127
  ? createPlanSubjectAtRef(cwd, commit, stableSlug, overview.path)
128
128
  : createDeliverySubjectAtRef(cwd, commit, stableSlug, overview.path);
129
- const evaluation = evaluateReviewEvents(events, {
129
+ let evaluation = evaluateReviewEvents(events, {
130
130
  purpose,
131
131
  blueprintSlug: stableSlug,
132
132
  subjectScheme: subject.scheme,
133
133
  subjectDigest: subject.digest,
134
134
  }, compromisedIds);
135
+ if (purpose === "plan" && evaluation.approvals.length === 0) {
136
+ // Digest-compatibility fallback: events recorded before the lifecycle
137
+ // stripped-key extension bound their subjectDigest to the legacy
138
+ // normalization. Accept those for the same tracked content; new events
139
+ // always record the current digest (see legacyNormalizePlanSubjectContent).
140
+ const legacySubject = createLegacyPlanSubjectAtRef(cwd, commit, stableSlug, overview.path);
141
+ if (legacySubject.digest !== subject.digest) {
142
+ evaluation = evaluateReviewEvents(events, {
143
+ purpose,
144
+ blueprintSlug: stableSlug,
145
+ subjectScheme: legacySubject.scheme,
146
+ subjectDigest: legacySubject.digest,
147
+ }, compromisedIds);
148
+ }
149
+ }
135
150
  return {
136
151
  commit,
137
152
  blueprintPath: overview.path,
@@ -3,11 +3,7 @@ import path from "node:path";
3
3
  import { NotInGitRepoError, withLock } from "#paths/state-root.js";
4
4
  import { writeJsonFileAtomic } from "#shared-utils/write-json-file.js";
5
5
  export const REVIEW_AVAILABILITY_SCHEMA_VERSION = 1;
6
- const FAILURE_CLASSIFICATIONS = new Set([
7
- "provider-limit-reached",
8
- "provider-unavailable",
9
- "provider-failed",
10
- ]);
6
+ const FAILURE_CLASSIFICATIONS = new Set(["provider-limit-reached", "provider-unavailable"]);
11
7
  const SUCCESS_CLASSIFICATIONS = new Set([
12
8
  "complete",
13
9
  "approved",
@@ -117,6 +113,10 @@ export function saveReviewAvailability(projectRoot, state) {
117
113
  function shouldSuppress(event) {
118
114
  return FAILURE_CLASSIFICATIONS.has(event.terminalClassification);
119
115
  }
116
+ function suppressedRecordStillApplies(record) {
117
+ return (record.terminalClassification !== undefined &&
118
+ FAILURE_CLASSIFICATIONS.has(record.terminalClassification));
119
+ }
120
120
  function isAvailabilitySuccess(event) {
121
121
  return SUCCESS_CLASSIFICATIONS.has(event.terminalClassification);
122
122
  }
@@ -155,6 +155,15 @@ function nextRecord(previous, event, input) {
155
155
  const success = isAvailabilitySuccess(event);
156
156
  if (!input.suppress) {
157
157
  const preserveSuppression = !success && previous?.suppressed === true;
158
+ if (preserveSuppression) {
159
+ return {
160
+ ...previous,
161
+ ...(event.model ? { lastModel: event.model } : {}),
162
+ ...(event.effort ? { lastEffort: event.effort } : {}),
163
+ ...(event.stage ? { lastStage: event.stage } : {}),
164
+ suppressed: true,
165
+ };
166
+ }
158
167
  return {
159
168
  ...common,
160
169
  ...(success
@@ -244,6 +253,10 @@ export function suppressedReviewers(state) {
244
253
  export function pruneExpiredAvailabilitySuppressions(state, nowMs = Date.now()) {
245
254
  const pruned = [];
246
255
  const pruneMap = (records) => Object.fromEntries(Object.entries(records).map(([key, record]) => {
256
+ if (record.suppressed && !suppressedRecordStillApplies(record)) {
257
+ pruned.push(key);
258
+ return [key, { ...record, suppressed: false }];
259
+ }
247
260
  if (record.suppressed &&
248
261
  record.retryAfterMs !== undefined &&
249
262
  record.lastFailureAt !== undefined) {
@@ -18,7 +18,22 @@ declare function updateField(hash: Hash, value: Buffer | string): void;
18
18
  export declare function createDeliverySubjectAtRef(cwd: string, ref: string, blueprintSlug: string, blueprintPath?: string): ReviewSubject;
19
19
  export declare function createSourceSubjectAtRef(cwd: string, ref: string): ReviewSubject;
20
20
  export declare function normalizePlanSubjectContent(content: Buffer | string): Buffer;
21
+ /**
22
+ * Plan-subject digest for in-memory (working-tree) blueprint content. Used to
23
+ * accept working-ledger review events that digest-bind the exact bytes being
24
+ * committed (pre-commit runs before those events reach any git ref).
25
+ */
26
+ export declare function createPlanSubjectDigestForContent(blueprintSlug: string, content: Buffer | string): string;
27
+ /**
28
+ * Digest-compatibility variant for review events recorded before the
29
+ * stripped-key extension of `neutralizeLifecycleFrontmatter`. Verification
30
+ * accepts a match against either normalization; new digests always use
31
+ * `normalizePlanSubjectContent`.
32
+ */
33
+ export declare function legacyNormalizePlanSubjectContent(content: Buffer | string): Buffer;
21
34
  export declare function createPlanSubjectAtRef(cwd: string, ref: string, blueprintSlug: string, blueprintPath?: string): ReviewSubject;
35
+ /** Plan subject digested with the pre-extension normalization (see above). */
36
+ export declare function createLegacyPlanSubjectAtRef(cwd: string, ref: string, blueprintSlug: string, blueprintPath?: string): ReviewSubject;
22
37
  export declare const reviewSubjectInternals: {
23
38
  isExcludedReviewEnvelope: typeof isExcludedReviewEnvelope;
24
39
  isReviewEvidencePath: typeof isReviewEvidencePath;
@@ -255,12 +255,39 @@ function neutralizePromotionGateResults(text) {
255
255
  }
256
256
  return lines.join("\n");
257
257
  }
258
- // Neutralize the lifecycle-owned frontmatter fields (`status:` value and the
259
- // `completed_at:` line) INSIDE the leading frontmatter block only. A body line
258
+ // Frontmatter keys removed wholesale by the normalizer (value + folded
259
+ // continuation lines). Each mirrors exactly one lifecycle writer:
260
+ // `completed_at` (upsertCompletedAt) and the `wp blueprint start` /
261
+ // progress-stamp fields (setBlueprintFrontmatterFields lifecycle intents).
262
+ const PLAN_LIFECYCLE_STRIPPED_KEYS = [
263
+ "completed_at",
264
+ "last_updated",
265
+ "progress",
266
+ "worktree_owner_id",
267
+ "worktree_owner_branch",
268
+ ];
269
+ // Neutralize the lifecycle-owned frontmatter fields (`status:` value plus the
270
+ // stripped keys above) INSIDE the leading frontmatter block only. A body line
260
271
  // that merely looks like one of these (e.g. task text reading
261
272
  // "completed_at: must remain unset until release") is plan substance and must
262
- // keep affecting the digest.
273
+ // keep affecting the digest. Stripping removes the key line and any indented
274
+ // continuation lines so folded (`>-`) values normalize away too.
263
275
  function neutralizeLifecycleFrontmatter(text) {
276
+ const match = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/u.exec(text);
277
+ if (!match)
278
+ return text;
279
+ let frontmatter = match[0];
280
+ frontmatter = frontmatter.replace(/^(status:\s*)(['"]?)[^'"\r\n]+?(['"]?)(\s*)$/m, `$1${PLAN_STATUS_PLACEHOLDER}$4`);
281
+ for (const key of PLAN_LIFECYCLE_STRIPPED_KEYS) {
282
+ frontmatter = frontmatter.replace(new RegExp(String.raw `^${key}:[^\n]*\n?(?:[ \t]+[^\n]*\n?)*`, "gmu"), "");
283
+ }
284
+ return `${frontmatter}${text.slice(match[0].length)}`;
285
+ }
286
+ // The pre-extension normalizer (status + completed_at only). Review events
287
+ // recorded before the stripped-key extension bound their subjectDigest to this
288
+ // normalization; verification accepts either digest so committed approvals are
289
+ // not stranded by the rollout. New events always record the current digest.
290
+ function legacyNeutralizeLifecycleFrontmatter(text) {
264
291
  const match = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/u.exec(text);
265
292
  if (!match)
266
293
  return text;
@@ -305,21 +332,85 @@ function neutralizeReadinessStamps(text) {
305
332
  // NO_CODE_CHANGE / GitLab patch-id semantics: approvals are re-derivable from
306
333
  // substance-only digests, not carried). Each rule mirrors exactly one
307
334
  // lifecycle writer, scoped to the location that writer owns:
308
- // - frontmatter `status:` value (updateFrontmatterStatus, blueprint mutations)
309
- // - frontmatter `completed_at:` line (upsertCompletedAt)
335
+ // - frontmatter status/progress/owner fields (blueprint lifecycle mutations)
336
+ // - frontmatter `completed_at:` line (upsertCompletedAt)
310
337
  // - Readiness Verdict `- verified-at:`/`- verified-head:` items (upsertReadinessValue)
311
338
  // - Promotion Gates "Last result" cells (updateGateLastResult)
312
339
  // Any other byte change — task text, acceptance criteria, scope, lookalike
313
340
  // lines outside the owned locations — still changes the digest and dismisses
314
341
  // prior plan approvals.
342
+ const PLAN_TASK_STATUS_PLACEHOLDER = "__wp-task-status__";
343
+ // Neutralize the task-execution state the lifecycle engine writes
344
+ // (updateTaskStatus, applyVerification): `**Status:** <status>` lines,
345
+ // acceptance checkbox marks, and `**Verification:**` +
346
+ // ```webpresso-evidence-v1 fenced evidence blocks. Task execution is lifecycle
347
+ // state, not plan substance — without this, the engine's own task_verify /
348
+ // finalize writes dismiss the plan approval and no completion commit can ever
349
+ // carry authority. Acceptance-criteria TEXT stays substance; only the check
350
+ // mark is neutralized.
351
+ function neutralizeTaskExecutionState(text) {
352
+ const lines = text.split("\n");
353
+ const out = [];
354
+ for (let index = 0; index < lines.length; index += 1) {
355
+ const line = lines[index];
356
+ if (/^\*\*Verification:\*\*\s*$/u.test(line)) {
357
+ // Skip the writer-owned block: "**Verification:**", blank(s), the
358
+ // evidence fence, and one trailing blank line.
359
+ let cursor = index + 1;
360
+ while (cursor < lines.length && lines[cursor].trim() === "")
361
+ cursor += 1;
362
+ if (/^```webpresso-evidence-v1\s*$/u.test(lines[cursor] ?? "")) {
363
+ cursor += 1;
364
+ while (cursor < lines.length && !/^```\s*$/u.test(lines[cursor]))
365
+ cursor += 1;
366
+ cursor += 1; // closing fence
367
+ if ((lines[cursor] ?? "").trim() === "")
368
+ cursor += 1;
369
+ index = cursor - 1;
370
+ continue;
371
+ }
372
+ }
373
+ if (/^\*\*Status:\*\* (?:todo|in-progress|blocked|done|dropped)\s*$/u.test(line)) {
374
+ out.push(`**Status:** ${PLAN_TASK_STATUS_PLACEHOLDER}`);
375
+ continue;
376
+ }
377
+ out.push(line.replace(/^(\s*)- \[[xX ]\] /u, "$1- [_] "));
378
+ }
379
+ return out.join("\n");
380
+ }
315
381
  export function normalizePlanSubjectContent(content) {
316
382
  let text = typeof content === "string" ? content : content.toString("utf8");
317
383
  text = neutralizeLifecycleFrontmatter(text);
318
384
  text = neutralizeReadinessStamps(text);
319
385
  text = neutralizePromotionGateResults(text);
386
+ text = neutralizeTaskExecutionState(text);
320
387
  return Buffer.from(text, "utf8");
321
388
  }
322
- export function createPlanSubjectAtRef(cwd, ref, blueprintSlug, blueprintPath) {
389
+ /**
390
+ * Plan-subject digest for in-memory (working-tree) blueprint content. Used to
391
+ * accept working-ledger review events that digest-bind the exact bytes being
392
+ * committed (pre-commit runs before those events reach any git ref).
393
+ */
394
+ export function createPlanSubjectDigestForContent(blueprintSlug, content) {
395
+ const hash = createHash("sha256").update(PLAN_DOMAIN);
396
+ updateField(hash, blueprintSlug);
397
+ updateField(hash, normalizePlanSubjectContent(content));
398
+ return hash.digest("hex");
399
+ }
400
+ /**
401
+ * Digest-compatibility variant for review events recorded before the
402
+ * stripped-key extension of `neutralizeLifecycleFrontmatter`. Verification
403
+ * accepts a match against either normalization; new digests always use
404
+ * `normalizePlanSubjectContent`.
405
+ */
406
+ export function legacyNormalizePlanSubjectContent(content) {
407
+ let text = typeof content === "string" ? content : content.toString("utf8");
408
+ text = legacyNeutralizeLifecycleFrontmatter(text);
409
+ text = neutralizeReadinessStamps(text);
410
+ text = neutralizePromotionGateResults(text);
411
+ return Buffer.from(text, "utf8");
412
+ }
413
+ function planSubjectAtRef(cwd, ref, blueprintSlug, normalize, blueprintPath) {
323
414
  const commit = resolveReviewCommit(cwd, ref);
324
415
  const entries = parseTree(cwd, commit);
325
416
  const blueprintRoot = findBlueprintRoot(entries, blueprintSlug, blueprintPath);
@@ -333,10 +424,7 @@ export function createPlanSubjectAtRef(cwd, ref, blueprintSlug, blueprintPath) {
333
424
  throw new Error("Tracked blueprint overview content is unavailable.");
334
425
  const hash = createHash("sha256").update(PLAN_DOMAIN);
335
426
  updateField(hash, blueprintSlug);
336
- // Hash normalized content. This redefines wp-blueprint-v1 in place, which is
337
- // safe pre-merge: no committed review-events.jsonl exists anywhere, so no
338
- // recorded plan digest can break.
339
- updateField(hash, normalizePlanSubjectContent(content));
427
+ updateField(hash, normalize(content));
340
428
  return {
341
429
  scheme: "wp-blueprint-v1",
342
430
  digest: hash.digest("hex"),
@@ -344,6 +432,13 @@ export function createPlanSubjectAtRef(cwd, ref, blueprintSlug, blueprintPath) {
344
432
  files: [overviewPath.toString("utf8")],
345
433
  };
346
434
  }
435
+ export function createPlanSubjectAtRef(cwd, ref, blueprintSlug, blueprintPath) {
436
+ return planSubjectAtRef(cwd, ref, blueprintSlug, normalizePlanSubjectContent, blueprintPath);
437
+ }
438
+ /** Plan subject digested with the pre-extension normalization (see above). */
439
+ export function createLegacyPlanSubjectAtRef(cwd, ref, blueprintSlug, blueprintPath) {
440
+ return planSubjectAtRef(cwd, ref, blueprintSlug, legacyNormalizePlanSubjectContent, blueprintPath);
441
+ }
347
442
  export const reviewSubjectInternals = {
348
443
  isExcludedReviewEnvelope,
349
444
  isReviewEvidencePath,
@@ -14,6 +14,7 @@ export interface ResolveRuntimeEnvironmentOptions {
14
14
  }
15
15
  export interface RuntimeSpawnOptions {
16
16
  readonly cwd?: string;
17
+ readonly envCwd?: string;
17
18
  readonly profile?: RuntimeSelector;
18
19
  readonly secretProfile?: string;
19
20
  readonly environment?: string;
@@ -82,9 +82,10 @@ export function resolveRuntimeEnvironment(options = {}) {
82
82
  }
83
83
  export function buildRuntimeSpawnOptions(options = {}) {
84
84
  const cwd = options.cwd ?? process.cwd();
85
+ const envCwd = options.envCwd ?? cwd;
85
86
  const cache = options.cache ?? createRuntimeEnvCache();
86
87
  const resolvedEnv = resolveRuntimeEnvironment({
87
- cwd,
88
+ cwd: envCwd,
88
89
  profile: options.profile,
89
90
  secretProfile: options.secretProfile,
90
91
  environment: options.environment,
@@ -11,6 +11,7 @@ export interface SecretGateCommandOptions {
11
11
  readonly command: string;
12
12
  readonly args?: readonly string[];
13
13
  readonly cwd?: string;
14
+ readonly commandCwd?: string;
14
15
  readonly timeoutMs?: number;
15
16
  readonly signal?: AbortSignal;
16
17
  readonly forceSecretGate?: boolean;
@@ -35,6 +35,7 @@ export function buildSecretGateCommand(options) {
35
35
  sink,
36
36
  "--profile",
37
37
  profile,
38
+ ...(options.commandCwd ? ["--command-cwd", options.commandCwd] : []),
38
39
  "--",
39
40
  options.command,
40
41
  ...(options.args ?? []),
@@ -45,9 +46,11 @@ export function runSecretGateCommand(options) {
45
46
  const timeoutMs = options.timeoutMs ?? 30_000;
46
47
  const maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
47
48
  const command = buildSecretGateCommand(options);
49
+ const runsInnerCommandDirectly = command.command === options.command;
50
+ const spawnCwd = runsInnerCommandDirectly && options.commandCwd ? options.commandCwd : options.cwd;
48
51
  return new Promise((resolve) => {
49
52
  const child = spawn(command.command, [...command.args], {
50
- cwd: options.cwd,
53
+ cwd: spawnCwd,
51
54
  env: buildRuntimeProcessEnv(options.cwd, process.env),
52
55
  detached: process.platform !== "win32",
53
56
  });
@@ -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",