@papi-ai/server 0.7.80 → 0.7.81

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.
@@ -28,6 +28,7 @@ __export(git_exports, {
28
28
  ensureTagAtHead: () => ensureTagAtHead,
29
29
  findTaskCommitsOnBase: () => findTaskCommitsOnBase,
30
30
  getBranchDiff: () => getBranchDiff,
31
+ getCommitFiles: () => getCommitFiles,
31
32
  getCommitsSinceTag: () => getCommitsSinceTag,
32
33
  getCurrentBranch: () => getCurrentBranch,
33
34
  getDocPathsTouchedOnBranch: () => getDocPathsTouchedOnBranch,
@@ -41,11 +42,13 @@ __export(git_exports, {
41
42
  getModifiedFiles: () => getModifiedFiles,
42
43
  getOriginRepoSlug: () => getOriginRepoSlug,
43
44
  getOriginUrl: () => getOriginUrl,
45
+ getPathsDifferingFrom: () => getPathsDifferingFrom,
44
46
  getPullRequestUrl: () => getPullRequestUrl,
45
47
  getRemoteBranchFiles: () => getRemoteBranchFiles,
46
48
  getRootCommitHash: () => getRootCommitHash,
47
49
  getStagedFiles: () => getStagedFiles,
48
50
  getTagTarget: () => getTagTarget,
51
+ getTaskDiff: () => getTaskDiff,
49
52
  getTaskIdsOnBranch: () => getTaskIdsOnBranch,
50
53
  getTrackedModifiedFiles: () => getTrackedModifiedFiles,
51
54
  getUnmergedBranches: () => getUnmergedBranches,
@@ -271,6 +274,50 @@ function getBranchDiff(cwd, base = "origin/main", maxBytes = 2e5) {
271
274
  }
272
275
  return "";
273
276
  }
277
+ function getTaskDiff(cwd, taskId, base = "origin/main", maxBytes = 2e5) {
278
+ const truncate = (out) => out.length > maxBytes ? `${out.slice(0, maxBytes)}
279
+
280
+ ... [diff truncated at ${Math.round(maxBytes / 1024)} KB]` : out;
281
+ try {
282
+ const shas = execFileSync(
283
+ "git",
284
+ ["log", "--all", "--format=%H", `--grep=${taskId})`, "--fixed-strings"],
285
+ { cwd, encoding: "utf-8", maxBuffer: 8 * 1024 * 1024 }
286
+ ).split("\n").map((s) => s.trim()).filter(Boolean);
287
+ if (shas.length > 0) {
288
+ const parts = [];
289
+ for (const sha of [...shas].reverse()) {
290
+ try {
291
+ const one = execFileSync("git", ["diff", `${sha}^..${sha}`], {
292
+ cwd,
293
+ encoding: "utf-8",
294
+ maxBuffer: 32 * 1024 * 1024
295
+ });
296
+ if (one) parts.push(one);
297
+ } catch {
298
+ }
299
+ }
300
+ const out = parts.join("\n");
301
+ if (out) {
302
+ const range = shas.length === 1 ? shas[0].slice(0, 8) : `${shas[shas.length - 1].slice(0, 8)}\u2026${shas[0].slice(0, 8)}`;
303
+ return {
304
+ diff: truncate(out),
305
+ scope: "task-commits",
306
+ detail: `${shas.length} commit(s) for ${taskId} (${range}), each against its own parent`
307
+ };
308
+ }
309
+ }
310
+ } catch {
311
+ }
312
+ const branch = getCurrentBranch(cwd);
313
+ const diff = getBranchDiff(cwd, base, maxBytes);
314
+ if (!diff) return { diff: "", scope: "none", detail: "no diff resolved" };
315
+ return {
316
+ diff,
317
+ scope: "whole-branch",
318
+ detail: `no commit naming ${taskId} was found, so this is the ENTIRE diff of ${branch ?? "the current branch"} vs ${base} \u2014 it may include other tasks' work, and may not include this task's`
319
+ };
320
+ }
274
321
  function getHeadCommitSubject(cwd) {
275
322
  try {
276
323
  const out = execFileSync("git", ["log", "-1", "--format=%s"], {
@@ -318,6 +365,28 @@ function branchExists(cwd, branch) {
318
365
  return false;
319
366
  }
320
367
  }
368
+ function getPathsDifferingFrom(cwd, target) {
369
+ try {
370
+ const out = execFileSync("git", ["diff", "--name-only", "HEAD", target], {
371
+ cwd,
372
+ encoding: "utf-8"
373
+ });
374
+ return out.split("\n").map((l) => l.trim()).filter(Boolean);
375
+ } catch {
376
+ return null;
377
+ }
378
+ }
379
+ function getCommitFiles(cwd, ref = "HEAD") {
380
+ try {
381
+ const out = execFileSync("git", ["show", "--name-only", "--pretty=format:", ref], {
382
+ cwd,
383
+ encoding: "utf-8"
384
+ });
385
+ return out.split("\n").map((l) => l.trim()).filter(Boolean);
386
+ } catch {
387
+ return null;
388
+ }
389
+ }
321
390
  function checkoutBranch(cwd, branch) {
322
391
  try {
323
392
  execFileSync("git", ["checkout", branch], { cwd, encoding: "utf-8" });
@@ -1272,7 +1341,7 @@ var init_proxy_adapter = __esm({
1272
1341
  "listContributorReleasePrs",
1273
1342
  "claimReview",
1274
1343
  "getSiblingAds",
1275
- "getSiblingRepoTasks"
1344
+ "getSiblingRepoTasks",
1276
1345
  // task-2828 (C339): attributed-intelligence analytics reader — pg-only that cycle.
1277
1346
  // task-2864 (C343): WIRED. getModelOutcomeStats now has an edge case handler (raw
1278
1347
  // SQL via postgres.js mirroring the pg query + inlined computeModelOutcomes bucketing)
@@ -1296,6 +1365,28 @@ var init_proxy_adapter = __esm({
1296
1365
  // (case handler + ALLOWED_METHODS/WRITE_METHODS entries), so it forwards for
1297
1366
  // hosted callers and persists a project-scoped cycle_progress_steps row. Removed
1298
1367
  // from NO_FORWARD (was the task-2484 pg-only gap) — hosted parity restored.
1368
+ //
1369
+ // (3) pg-only optimisations probed STRUCTURALLY with a local fallback beside them.
1370
+ // These are the dangerous class: the caller asks `typeof adapter.X === 'function'`
1371
+ // to decide whether the fast path exists, and the get-trap answers "yes" for any
1372
+ // name absent from this set. The probe then passes, the call forwards, and the
1373
+ // edge 403s — while a working fallback sits a few lines below, unreachable.
1374
+ // A method belongs here when BOTH are true: it is probed by `typeof` rather than
1375
+ // called unconditionally, and the probe's else-branch is a real fallback.
1376
+ //
1377
+ // task-3290 (C361): allocateActiveDecision + applyActiveDecisionUpdates are pg-only
1378
+ // (atomic id allocation; transactional batch apply). Both are probed in
1379
+ // services/strategy.ts — asDecisionIdAllocator and asDecisionBatchApplier — and both
1380
+ // have read-then-write / sequential fallbacks. Hosted users hit a 403 minting ANY
1381
+ // Active Decision via setup's AD seed or strategy_change until these were listed.
1382
+ "allocateActiveDecision",
1383
+ "applyActiveDecisionUpdates",
1384
+ // task-3290 (C361), same sweep: getLastZoomOutCycle was documented in the parity
1385
+ // ledger as "proxy NO_FORWARD → safe no-op" while NOT being in this set. It is
1386
+ // optional-chain probed in services/health.ts and wrapped in try/catch, so it
1387
+ // degraded rather than crashed — but every hosted `orient` paid a round-trip to
1388
+ // earn a silent 403. Listing it makes the ledger's claim true and skips the trip.
1389
+ "getLastZoomOutCycle"
1299
1390
  ]);
1300
1391
  ProxyPapiAdapter = class _ProxyPapiAdapter {
1301
1392
  endpoint;
@@ -1827,6 +1918,21 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
1827
1918
  updateDogfoodEntryStatus(id, status, linkedTaskId) {
1828
1919
  return this.invoke("updateDogfoodEntryStatus", [id, status, linkedTaskId]);
1829
1920
  }
1921
+ // --- Project conventions (task-3271) ---
1922
+ //
1923
+ // Wired to the edge handler from the start rather than parked in NO_FORWARD.
1924
+ // The hosted remote connector is the only install path an external user has,
1925
+ // so a local-only conventions store would be a feature nobody in the actual
1926
+ // user base can reach.
1927
+ listConventions() {
1928
+ return this.invoke("listConventions");
1929
+ }
1930
+ createConvention(convention) {
1931
+ return this.invoke("createConvention", [convention]);
1932
+ }
1933
+ deleteConvention(id) {
1934
+ return this.invoke("deleteConvention", [id]);
1935
+ }
1830
1936
  // --- Harness inventory (task-1896) ---
1831
1937
  getHarnessInventory() {
1832
1938
  return this.invoke("getHarnessInventory");