@agent-native/core 0.49.17 → 0.49.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/recap.js CHANGED
@@ -2256,21 +2256,30 @@ function recoverRecapFailureEnv(env = process.env) {
2256
2256
  return recovered;
2257
2257
  }
2258
2258
  /**
2259
- * Files that, if a PR touches them, would let that PR rewrite the workflow,
2260
- * skill, or agent config the trusted recap job loads. The workflow runs the
2261
- * recap CLI from trusted base-branch source (or an installed package), so normal
2262
- * package code such as `packages/core/**` can be recapped without executing
2263
- * PR-modified CLI code.
2259
+ * Files that, if a PR touches them, would let that PR rewrite repo-pinned skill
2260
+ * instructions or agent config the trusted recap job loads. The workflow runs
2261
+ * the recap CLI from trusted base-branch source (or an installed package), so
2262
+ * normal package code such as `packages/core/**` and recap workflow YAML can be
2263
+ * recapped without executing PR-modified CLI code.
2264
2264
  */
2265
- export function isRecapSensitivePath(p) {
2266
- if (p === ".github/workflows/pr-visual-recap.yml" ||
2267
- /(^|\/)skills\/visual-(recap|plan|plans)\//.test(p) ||
2268
- /(^|\/)\.claude\//.test(p) ||
2265
+ function normalizeRecapSkillSourceMode(value) {
2266
+ return (value || "auto").toLowerCase();
2267
+ }
2268
+ function isRepoPinnedRecapSkillSource(value) {
2269
+ return normalizeRecapSkillSourceMode(value) === "repo";
2270
+ }
2271
+ export function isRecapSensitivePath(p, options = {}) {
2272
+ const skillSource = options.skillSource;
2273
+ if (/(^|\/)\.claude\//.test(p) ||
2269
2274
  /(^|\/)CLAUDE\.md$/.test(p) ||
2270
2275
  /(^|\/)AGENTS\.md$/.test(p) ||
2271
2276
  /(^|\/)\.mcp\.json$/.test(p)) {
2272
2277
  return true;
2273
2278
  }
2279
+ if (isRepoPinnedRecapSkillSource(skillSource) &&
2280
+ /(^|\/)skills\/visual-(recap|plan|plans)\//.test(p)) {
2281
+ return true;
2282
+ }
2274
2283
  return false;
2275
2284
  }
2276
2285
  /**
@@ -2330,12 +2339,19 @@ export function evaluateRecapGate(input) {
2330
2339
  if (model && !/^[a-zA-Z0-9._-]{1,80}$/.test(model)) {
2331
2340
  reasons.push("invalid VISUAL_RECAP_MODEL value (must match [a-zA-Z0-9._-]{1,80})");
2332
2341
  }
2333
- // Self-modifying guard: if this PR changes the workflow, the
2334
- // visual-recap/visual-plan skill, or any agent config the runner would load
2335
- // (.claude/**, CLAUDE.md, .mcp.json), skip the ENTIRE job — not just the
2336
- // agent — so a PR can never rewrite what runs (skill, hooks, settings) and
2337
- // exfiltrate the publish/API secrets.
2338
- const hits = input.changedFiles.filter((p) => isRecapSensitivePath(p));
2342
+ const skillSource = normalizeRecapSkillSourceMode(input.skillSource);
2343
+ if (skillSource && !["auto", "latest", "repo"].includes(skillSource)) {
2344
+ reasons.push('invalid VISUAL_RECAP_SKILL_SOURCE value (expected "auto", "latest", or "repo")');
2345
+ }
2346
+ // Self-modifying guard: if this PR changes the visual-recap/visual-plan skill
2347
+ // when CI is explicitly pinned to repo-local skill instructions, or any agent
2348
+ // config the runner would load (.claude/**, CLAUDE.md, AGENTS.md, .mcp.json),
2349
+ // skip the ENTIRE job — not just the agent — so a PR can never rewrite what
2350
+ // the agent loads (skill, hooks, settings) and exfiltrate the publish/API
2351
+ // secrets. In the default auto/latest modes the recap prompt comes from the
2352
+ // trusted bundled skill, so visual skill and recap workflow files are ordinary
2353
+ // reviewed content and may be recapped.
2354
+ const hits = input.changedFiles.filter((p) => isRecapSensitivePath(p, { skillSource }));
2339
2355
  if (hits.length) {
2340
2356
  reasons.push(`PR modifies recap-control files (${hits.slice(0, 3).join(", ")}${hits.length > 3 ? ", …" : ""}) — skipping so untrusted PR code never runs with secrets`);
2341
2357
  }
@@ -2428,6 +2444,7 @@ async function runGate() {
2428
2444
  hasOpenai: process.env.HAS_OPENAI === "true",
2429
2445
  agentRaw: process.env.AGENT,
2430
2446
  model: process.env.VISUAL_RECAP_MODEL,
2447
+ skillSource: process.env.VISUAL_RECAP_SKILL_SOURCE,
2431
2448
  changedFiles,
2432
2449
  });
2433
2450
  // If listing PR files failed, append the same fail-closed reason the inline
@@ -3022,7 +3039,7 @@ Usage:
3022
3039
  VISUAL_RECAP_MODEL), the repo from $GITHUB_REPOSITORY, and the PR's changed
3023
3040
  files from the GitHub REST API (paged, with GH_TOKEN/GITHUB_TOKEN). Skips
3024
3041
  drafts, forks, bot authors, the missing-secret case, an invalid agent/model,
3025
- and any PR that touches recap-control files (the workflow, the skill,
3042
+ and any PR that touches recap-control files (repo-pinned skill instructions,
3026
3043
  .claude/**, CLAUDE.md, AGENTS.md, .mcp.json) — failing CLOSED on any
3027
3044
  file-list error. Writes run=<true|false> and agent=<claude|codex> to
3028
3045
  $GITHUB_OUTPUT.