@papi-ai/server 0.7.81 → 0.7.82
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/backfill-cycle-metrics.js +192 -1
- package/dist/index.js +375 -82
- package/package.json +1 -1
|
@@ -63,6 +63,7 @@ __export(git_exports, {
|
|
|
63
63
|
isGhAvailable: () => isGhAvailable,
|
|
64
64
|
isGitAvailable: () => isGitAvailable,
|
|
65
65
|
isGitRepo: () => isGitRepo,
|
|
66
|
+
isPathCommittedAnywhere: () => isPathCommittedAnywhere,
|
|
66
67
|
isPathIgnored: () => isPathIgnored,
|
|
67
68
|
isPathTracked: () => isPathTracked,
|
|
68
69
|
listGroupedCycleBranches: () => listGroupedCycleBranches,
|
|
@@ -102,6 +103,22 @@ function isGitRepo(cwd) {
|
|
|
102
103
|
return false;
|
|
103
104
|
}
|
|
104
105
|
}
|
|
106
|
+
function isPathCommittedAnywhere(cwd, path3) {
|
|
107
|
+
const run = (args) => {
|
|
108
|
+
try {
|
|
109
|
+
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const commit = run(["log", "--all", "--format=%H", "-1", "--", path3]);
|
|
115
|
+
if (!commit) return false;
|
|
116
|
+
const committedBlob = run(["rev-parse", `${commit}:${path3}`]);
|
|
117
|
+
if (!committedBlob) return false;
|
|
118
|
+
const onDiskBlob = run(["hash-object", "--", path3]);
|
|
119
|
+
if (!onDiskBlob) return false;
|
|
120
|
+
return committedBlob === onDiskBlob;
|
|
121
|
+
}
|
|
105
122
|
function isPathTracked(cwd, path3) {
|
|
106
123
|
try {
|
|
107
124
|
execFileSync("git", ["ls-files", "--error-unmatch", "--", path3], {
|
|
@@ -1285,7 +1302,7 @@ function wrapWithForwarding(instance) {
|
|
|
1285
1302
|
function createProxyAdapter(config) {
|
|
1286
1303
|
return wrapWithForwarding(new ProxyPapiAdapter(config));
|
|
1287
1304
|
}
|
|
1288
|
-
var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, NO_FORWARD, ProxyPapiAdapter;
|
|
1305
|
+
var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, HOSTED_SERVED, NO_FORWARD, ProxyPapiAdapter;
|
|
1289
1306
|
var init_proxy_adapter = __esm({
|
|
1290
1307
|
"src/proxy-adapter.ts"() {
|
|
1291
1308
|
"use strict";
|
|
@@ -1313,6 +1330,156 @@ var init_proxy_adapter = __esm({
|
|
|
1313
1330
|
"getRecentReviews",
|
|
1314
1331
|
"getActiveDecisions"
|
|
1315
1332
|
]);
|
|
1333
|
+
HOSTED_SERVED = /* @__PURE__ */ new Set([
|
|
1334
|
+
"actionRecommendation",
|
|
1335
|
+
"addAgendaTopic",
|
|
1336
|
+
"addContributorByEmail",
|
|
1337
|
+
"appendBuildReport",
|
|
1338
|
+
"appendCycleLearnings",
|
|
1339
|
+
"appendCycleMetrics",
|
|
1340
|
+
"appendDecisionEvent",
|
|
1341
|
+
"appendToolMetric",
|
|
1342
|
+
"archiveTasks",
|
|
1343
|
+
"claimTask",
|
|
1344
|
+
"clearPendingReviewResponse",
|
|
1345
|
+
"compressBuildReports",
|
|
1346
|
+
"compressCycleLog",
|
|
1347
|
+
"confirmPendingActiveDecisions",
|
|
1348
|
+
"correctLatestBuildReportEffort",
|
|
1349
|
+
"countDueOwnerActions",
|
|
1350
|
+
"countNudgedOwnerActions",
|
|
1351
|
+
"countOpenOwnerActions",
|
|
1352
|
+
"countOwnerActionsBlockingTasks",
|
|
1353
|
+
"countPlanRunsForCycle",
|
|
1354
|
+
"createConvention",
|
|
1355
|
+
"createCycle",
|
|
1356
|
+
"createHorizon",
|
|
1357
|
+
"createOwnerAction",
|
|
1358
|
+
"createStage",
|
|
1359
|
+
"createTask",
|
|
1360
|
+
"deleteActiveDecision",
|
|
1361
|
+
"deleteConvention",
|
|
1362
|
+
"deleteDoc",
|
|
1363
|
+
"dismissRecommendation",
|
|
1364
|
+
"findPendingDocActionsForTask",
|
|
1365
|
+
"getActiveDecisions",
|
|
1366
|
+
"getActiveStage",
|
|
1367
|
+
"getBuildReportCountForTask",
|
|
1368
|
+
"getBuildReportsSince",
|
|
1369
|
+
"getContextHashes",
|
|
1370
|
+
"getContextUtilisation",
|
|
1371
|
+
"getCostSnapshots",
|
|
1372
|
+
"getCostSummary",
|
|
1373
|
+
"getCurrentNorthStar",
|
|
1374
|
+
"getCycleHealth",
|
|
1375
|
+
"getCycleLearningPatterns",
|
|
1376
|
+
"getCycleLearnings",
|
|
1377
|
+
"getCycleLog",
|
|
1378
|
+
"getCycleLogSince",
|
|
1379
|
+
"getDecisionEvents",
|
|
1380
|
+
"getDecisionEventsSince",
|
|
1381
|
+
"getDecisionScorePatterns",
|
|
1382
|
+
"getDecisionScores",
|
|
1383
|
+
"getDecisionUsage",
|
|
1384
|
+
"getDoc",
|
|
1385
|
+
"getDocBody",
|
|
1386
|
+
"getDocBodyUsage",
|
|
1387
|
+
"getDogfoodLog",
|
|
1388
|
+
"getEstimationCalibration",
|
|
1389
|
+
"getFailedBuildAttemptsForTask",
|
|
1390
|
+
"getHarnessInventory",
|
|
1391
|
+
"getHarnessState",
|
|
1392
|
+
"getLastStrategyReviewCycle",
|
|
1393
|
+
"getLatestDecisionScores",
|
|
1394
|
+
"getModelOutcomeStats",
|
|
1395
|
+
"getModuleEstimationStats",
|
|
1396
|
+
"getNorthStarSetCycle",
|
|
1397
|
+
"getNorthStarStaleness",
|
|
1398
|
+
"getOwnerIdentity",
|
|
1399
|
+
"getPendingAgendaTopics",
|
|
1400
|
+
"getPendingRecommendations",
|
|
1401
|
+
"getPendingReviewResponse",
|
|
1402
|
+
"getPlanContextSummary",
|
|
1403
|
+
"getProjectInfo",
|
|
1404
|
+
"getProjectOwnerUserId",
|
|
1405
|
+
"getRecentBuildReports",
|
|
1406
|
+
"getRecentReviews",
|
|
1407
|
+
"getRecentTaskComments",
|
|
1408
|
+
"getRecommendationEffectiveness",
|
|
1409
|
+
"getStrategyReviews",
|
|
1410
|
+
"getTask",
|
|
1411
|
+
"getTasks",
|
|
1412
|
+
"getToolCallCount",
|
|
1413
|
+
"getUnactionedDogfoodEntries",
|
|
1414
|
+
"getUnnotifiedResolvedFeedback",
|
|
1415
|
+
"hasToolMilestone",
|
|
1416
|
+
"insertPlanRun",
|
|
1417
|
+
"insertToolRun",
|
|
1418
|
+
"linkOwnerActionToTask",
|
|
1419
|
+
"linkPhasesToStage",
|
|
1420
|
+
"listContributors",
|
|
1421
|
+
"listConventions",
|
|
1422
|
+
"listMyBugReports",
|
|
1423
|
+
"listOwnerActionsForBlockerScan",
|
|
1424
|
+
"logEntityReferences",
|
|
1425
|
+
"markAgendaTopicsAddressed",
|
|
1426
|
+
"markCycleLearningResolved",
|
|
1427
|
+
"markFeedbackNotified",
|
|
1428
|
+
"moveTask",
|
|
1429
|
+
"planWriteBack",
|
|
1430
|
+
"projectExists",
|
|
1431
|
+
"queryBoard",
|
|
1432
|
+
"readCycleMetrics",
|
|
1433
|
+
"readCycles",
|
|
1434
|
+
"readCycles",
|
|
1435
|
+
"readDiscoveryCanvas",
|
|
1436
|
+
"readHorizons",
|
|
1437
|
+
"readPhases",
|
|
1438
|
+
"readPlanningLog",
|
|
1439
|
+
"readProductBrief",
|
|
1440
|
+
"readRegistries",
|
|
1441
|
+
"readStages",
|
|
1442
|
+
"readToolMetrics",
|
|
1443
|
+
"recordProgressStep",
|
|
1444
|
+
"recordTransition",
|
|
1445
|
+
"registerDoc",
|
|
1446
|
+
"removeContributorByEmail",
|
|
1447
|
+
"reorderDocs",
|
|
1448
|
+
"replaceHarnessInventory",
|
|
1449
|
+
"resolveLearningsForDoneTasks",
|
|
1450
|
+
"savePendingReviewResponse",
|
|
1451
|
+
"searchDocs",
|
|
1452
|
+
"setCriterionMet",
|
|
1453
|
+
"setCycleHealth",
|
|
1454
|
+
"setHarnessState",
|
|
1455
|
+
"setProjectPapiDir",
|
|
1456
|
+
"storeDocBody",
|
|
1457
|
+
"submitBugReport",
|
|
1458
|
+
"unclaimTask",
|
|
1459
|
+
"updateActiveDecision",
|
|
1460
|
+
"updateCycleLearningActionRef",
|
|
1461
|
+
"updateDiscoveryCanvas",
|
|
1462
|
+
"updateDocAction",
|
|
1463
|
+
"updateDocStatus",
|
|
1464
|
+
"updateDogfoodEntryStatus",
|
|
1465
|
+
"updateHorizonStatus",
|
|
1466
|
+
"updatePhaseStatus",
|
|
1467
|
+
"updateProductBrief",
|
|
1468
|
+
"updateRegistries",
|
|
1469
|
+
"updateStageExitCriteria",
|
|
1470
|
+
"updateStageStatus",
|
|
1471
|
+
"updateTask",
|
|
1472
|
+
"updateTaskStatus",
|
|
1473
|
+
"upsertActiveDecision",
|
|
1474
|
+
"upsertNorthStar",
|
|
1475
|
+
"writeCycleLogEntry",
|
|
1476
|
+
"writeDecisionScore",
|
|
1477
|
+
"writeDogfoodEntries",
|
|
1478
|
+
"writePhases",
|
|
1479
|
+
"writeRecommendation",
|
|
1480
|
+
"writeReview",
|
|
1481
|
+
"writeStrategyReview"
|
|
1482
|
+
]);
|
|
1316
1483
|
NO_FORWARD = /* @__PURE__ */ new Set([
|
|
1317
1484
|
// (1) local-only
|
|
1318
1485
|
"close",
|
|
@@ -1399,6 +1566,30 @@ var init_proxy_adapter = __esm({
|
|
|
1399
1566
|
this.projectId = config.projectId ?? "";
|
|
1400
1567
|
this.onAuthRejected = config.onAuthRejected;
|
|
1401
1568
|
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Does the hosted path genuinely serve `name`? (task-3022, C362)
|
|
1571
|
+
*
|
|
1572
|
+
* This is the honest answer to the question `typeof adapter.name === 'function'`
|
|
1573
|
+
* USED to ask and could not answer: the get-trap below manufactures a function
|
|
1574
|
+
* for any name absent from NO_FORWARD, so a structural probe reads true for
|
|
1575
|
+
* pg-only methods and the caller then earns a 403 from the edge.
|
|
1576
|
+
*
|
|
1577
|
+
* Answered from NO_FORWARD itself, deliberately — the same set the forwarder
|
|
1578
|
+
* consults — so this can never disagree with what an actual call would do. A
|
|
1579
|
+
* name in NO_FORWARD is either local-only or not yet wired hosted; either way
|
|
1580
|
+
* the caller must take its fallback. The proxy-parity test guarantees every
|
|
1581
|
+
* method is an explicit wrapper, in NO_FORWARD, or in the edge allowlist, so
|
|
1582
|
+
* "not in NO_FORWARD" is a sound proxy for "served hosted".
|
|
1583
|
+
*
|
|
1584
|
+
* A REAL method, not a forwarded one: Reflect.get in the trap returns it before
|
|
1585
|
+
* the forwarding branch is reached, so callers get this implementation rather
|
|
1586
|
+
* than a forwarder that would POST "supportsMethod" to the edge.
|
|
1587
|
+
*/
|
|
1588
|
+
supportsMethod(name) {
|
|
1589
|
+
if (NO_FORWARD.has(name)) return false;
|
|
1590
|
+
if (name in _ProxyPapiAdapter.prototype) return true;
|
|
1591
|
+
return HOSTED_SERVED.has(name);
|
|
1592
|
+
}
|
|
1402
1593
|
/**
|
|
1403
1594
|
* task-1773: bearer-only auth probe. Hits the USER-scoped `project-list` route
|
|
1404
1595
|
* (no projectId needed), so it answers exactly one question: does the proxy
|
package/dist/index.js
CHANGED
|
@@ -64,6 +64,7 @@ __export(git_exports, {
|
|
|
64
64
|
isGhAvailable: () => isGhAvailable,
|
|
65
65
|
isGitAvailable: () => isGitAvailable,
|
|
66
66
|
isGitRepo: () => isGitRepo,
|
|
67
|
+
isPathCommittedAnywhere: () => isPathCommittedAnywhere,
|
|
67
68
|
isPathIgnored: () => isPathIgnored,
|
|
68
69
|
isPathTracked: () => isPathTracked,
|
|
69
70
|
listGroupedCycleBranches: () => listGroupedCycleBranches,
|
|
@@ -103,6 +104,22 @@ function isGitRepo(cwd) {
|
|
|
103
104
|
return false;
|
|
104
105
|
}
|
|
105
106
|
}
|
|
107
|
+
function isPathCommittedAnywhere(cwd, path7) {
|
|
108
|
+
const run = (args) => {
|
|
109
|
+
try {
|
|
110
|
+
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const commit = run(["log", "--all", "--format=%H", "-1", "--", path7]);
|
|
116
|
+
if (!commit) return false;
|
|
117
|
+
const committedBlob = run(["rev-parse", `${commit}:${path7}`]);
|
|
118
|
+
if (!committedBlob) return false;
|
|
119
|
+
const onDiskBlob = run(["hash-object", "--", path7]);
|
|
120
|
+
if (!onDiskBlob) return false;
|
|
121
|
+
return committedBlob === onDiskBlob;
|
|
122
|
+
}
|
|
106
123
|
function isPathTracked(cwd, path7) {
|
|
107
124
|
try {
|
|
108
125
|
execFileSync("git", ["ls-files", "--error-unmatch", "--", path7], {
|
|
@@ -1342,7 +1359,7 @@ function wrapWithForwarding(instance) {
|
|
|
1342
1359
|
function createProxyAdapter(config2) {
|
|
1343
1360
|
return wrapWithForwarding(new ProxyPapiAdapter(config2));
|
|
1344
1361
|
}
|
|
1345
|
-
var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, NO_FORWARD, ProxyPapiAdapter;
|
|
1362
|
+
var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, HOSTED_SERVED, NO_FORWARD, ProxyPapiAdapter;
|
|
1346
1363
|
var init_proxy_adapter = __esm({
|
|
1347
1364
|
"src/proxy-adapter.ts"() {
|
|
1348
1365
|
"use strict";
|
|
@@ -1370,6 +1387,156 @@ var init_proxy_adapter = __esm({
|
|
|
1370
1387
|
"getRecentReviews",
|
|
1371
1388
|
"getActiveDecisions"
|
|
1372
1389
|
]);
|
|
1390
|
+
HOSTED_SERVED = /* @__PURE__ */ new Set([
|
|
1391
|
+
"actionRecommendation",
|
|
1392
|
+
"addAgendaTopic",
|
|
1393
|
+
"addContributorByEmail",
|
|
1394
|
+
"appendBuildReport",
|
|
1395
|
+
"appendCycleLearnings",
|
|
1396
|
+
"appendCycleMetrics",
|
|
1397
|
+
"appendDecisionEvent",
|
|
1398
|
+
"appendToolMetric",
|
|
1399
|
+
"archiveTasks",
|
|
1400
|
+
"claimTask",
|
|
1401
|
+
"clearPendingReviewResponse",
|
|
1402
|
+
"compressBuildReports",
|
|
1403
|
+
"compressCycleLog",
|
|
1404
|
+
"confirmPendingActiveDecisions",
|
|
1405
|
+
"correctLatestBuildReportEffort",
|
|
1406
|
+
"countDueOwnerActions",
|
|
1407
|
+
"countNudgedOwnerActions",
|
|
1408
|
+
"countOpenOwnerActions",
|
|
1409
|
+
"countOwnerActionsBlockingTasks",
|
|
1410
|
+
"countPlanRunsForCycle",
|
|
1411
|
+
"createConvention",
|
|
1412
|
+
"createCycle",
|
|
1413
|
+
"createHorizon",
|
|
1414
|
+
"createOwnerAction",
|
|
1415
|
+
"createStage",
|
|
1416
|
+
"createTask",
|
|
1417
|
+
"deleteActiveDecision",
|
|
1418
|
+
"deleteConvention",
|
|
1419
|
+
"deleteDoc",
|
|
1420
|
+
"dismissRecommendation",
|
|
1421
|
+
"findPendingDocActionsForTask",
|
|
1422
|
+
"getActiveDecisions",
|
|
1423
|
+
"getActiveStage",
|
|
1424
|
+
"getBuildReportCountForTask",
|
|
1425
|
+
"getBuildReportsSince",
|
|
1426
|
+
"getContextHashes",
|
|
1427
|
+
"getContextUtilisation",
|
|
1428
|
+
"getCostSnapshots",
|
|
1429
|
+
"getCostSummary",
|
|
1430
|
+
"getCurrentNorthStar",
|
|
1431
|
+
"getCycleHealth",
|
|
1432
|
+
"getCycleLearningPatterns",
|
|
1433
|
+
"getCycleLearnings",
|
|
1434
|
+
"getCycleLog",
|
|
1435
|
+
"getCycleLogSince",
|
|
1436
|
+
"getDecisionEvents",
|
|
1437
|
+
"getDecisionEventsSince",
|
|
1438
|
+
"getDecisionScorePatterns",
|
|
1439
|
+
"getDecisionScores",
|
|
1440
|
+
"getDecisionUsage",
|
|
1441
|
+
"getDoc",
|
|
1442
|
+
"getDocBody",
|
|
1443
|
+
"getDocBodyUsage",
|
|
1444
|
+
"getDogfoodLog",
|
|
1445
|
+
"getEstimationCalibration",
|
|
1446
|
+
"getFailedBuildAttemptsForTask",
|
|
1447
|
+
"getHarnessInventory",
|
|
1448
|
+
"getHarnessState",
|
|
1449
|
+
"getLastStrategyReviewCycle",
|
|
1450
|
+
"getLatestDecisionScores",
|
|
1451
|
+
"getModelOutcomeStats",
|
|
1452
|
+
"getModuleEstimationStats",
|
|
1453
|
+
"getNorthStarSetCycle",
|
|
1454
|
+
"getNorthStarStaleness",
|
|
1455
|
+
"getOwnerIdentity",
|
|
1456
|
+
"getPendingAgendaTopics",
|
|
1457
|
+
"getPendingRecommendations",
|
|
1458
|
+
"getPendingReviewResponse",
|
|
1459
|
+
"getPlanContextSummary",
|
|
1460
|
+
"getProjectInfo",
|
|
1461
|
+
"getProjectOwnerUserId",
|
|
1462
|
+
"getRecentBuildReports",
|
|
1463
|
+
"getRecentReviews",
|
|
1464
|
+
"getRecentTaskComments",
|
|
1465
|
+
"getRecommendationEffectiveness",
|
|
1466
|
+
"getStrategyReviews",
|
|
1467
|
+
"getTask",
|
|
1468
|
+
"getTasks",
|
|
1469
|
+
"getToolCallCount",
|
|
1470
|
+
"getUnactionedDogfoodEntries",
|
|
1471
|
+
"getUnnotifiedResolvedFeedback",
|
|
1472
|
+
"hasToolMilestone",
|
|
1473
|
+
"insertPlanRun",
|
|
1474
|
+
"insertToolRun",
|
|
1475
|
+
"linkOwnerActionToTask",
|
|
1476
|
+
"linkPhasesToStage",
|
|
1477
|
+
"listContributors",
|
|
1478
|
+
"listConventions",
|
|
1479
|
+
"listMyBugReports",
|
|
1480
|
+
"listOwnerActionsForBlockerScan",
|
|
1481
|
+
"logEntityReferences",
|
|
1482
|
+
"markAgendaTopicsAddressed",
|
|
1483
|
+
"markCycleLearningResolved",
|
|
1484
|
+
"markFeedbackNotified",
|
|
1485
|
+
"moveTask",
|
|
1486
|
+
"planWriteBack",
|
|
1487
|
+
"projectExists",
|
|
1488
|
+
"queryBoard",
|
|
1489
|
+
"readCycleMetrics",
|
|
1490
|
+
"readCycles",
|
|
1491
|
+
"readCycles",
|
|
1492
|
+
"readDiscoveryCanvas",
|
|
1493
|
+
"readHorizons",
|
|
1494
|
+
"readPhases",
|
|
1495
|
+
"readPlanningLog",
|
|
1496
|
+
"readProductBrief",
|
|
1497
|
+
"readRegistries",
|
|
1498
|
+
"readStages",
|
|
1499
|
+
"readToolMetrics",
|
|
1500
|
+
"recordProgressStep",
|
|
1501
|
+
"recordTransition",
|
|
1502
|
+
"registerDoc",
|
|
1503
|
+
"removeContributorByEmail",
|
|
1504
|
+
"reorderDocs",
|
|
1505
|
+
"replaceHarnessInventory",
|
|
1506
|
+
"resolveLearningsForDoneTasks",
|
|
1507
|
+
"savePendingReviewResponse",
|
|
1508
|
+
"searchDocs",
|
|
1509
|
+
"setCriterionMet",
|
|
1510
|
+
"setCycleHealth",
|
|
1511
|
+
"setHarnessState",
|
|
1512
|
+
"setProjectPapiDir",
|
|
1513
|
+
"storeDocBody",
|
|
1514
|
+
"submitBugReport",
|
|
1515
|
+
"unclaimTask",
|
|
1516
|
+
"updateActiveDecision",
|
|
1517
|
+
"updateCycleLearningActionRef",
|
|
1518
|
+
"updateDiscoveryCanvas",
|
|
1519
|
+
"updateDocAction",
|
|
1520
|
+
"updateDocStatus",
|
|
1521
|
+
"updateDogfoodEntryStatus",
|
|
1522
|
+
"updateHorizonStatus",
|
|
1523
|
+
"updatePhaseStatus",
|
|
1524
|
+
"updateProductBrief",
|
|
1525
|
+
"updateRegistries",
|
|
1526
|
+
"updateStageExitCriteria",
|
|
1527
|
+
"updateStageStatus",
|
|
1528
|
+
"updateTask",
|
|
1529
|
+
"updateTaskStatus",
|
|
1530
|
+
"upsertActiveDecision",
|
|
1531
|
+
"upsertNorthStar",
|
|
1532
|
+
"writeCycleLogEntry",
|
|
1533
|
+
"writeDecisionScore",
|
|
1534
|
+
"writeDogfoodEntries",
|
|
1535
|
+
"writePhases",
|
|
1536
|
+
"writeRecommendation",
|
|
1537
|
+
"writeReview",
|
|
1538
|
+
"writeStrategyReview"
|
|
1539
|
+
]);
|
|
1373
1540
|
NO_FORWARD = /* @__PURE__ */ new Set([
|
|
1374
1541
|
// (1) local-only
|
|
1375
1542
|
"close",
|
|
@@ -1456,6 +1623,30 @@ var init_proxy_adapter = __esm({
|
|
|
1456
1623
|
this.projectId = config2.projectId ?? "";
|
|
1457
1624
|
this.onAuthRejected = config2.onAuthRejected;
|
|
1458
1625
|
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Does the hosted path genuinely serve `name`? (task-3022, C362)
|
|
1628
|
+
*
|
|
1629
|
+
* This is the honest answer to the question `typeof adapter.name === 'function'`
|
|
1630
|
+
* USED to ask and could not answer: the get-trap below manufactures a function
|
|
1631
|
+
* for any name absent from NO_FORWARD, so a structural probe reads true for
|
|
1632
|
+
* pg-only methods and the caller then earns a 403 from the edge.
|
|
1633
|
+
*
|
|
1634
|
+
* Answered from NO_FORWARD itself, deliberately — the same set the forwarder
|
|
1635
|
+
* consults — so this can never disagree with what an actual call would do. A
|
|
1636
|
+
* name in NO_FORWARD is either local-only or not yet wired hosted; either way
|
|
1637
|
+
* the caller must take its fallback. The proxy-parity test guarantees every
|
|
1638
|
+
* method is an explicit wrapper, in NO_FORWARD, or in the edge allowlist, so
|
|
1639
|
+
* "not in NO_FORWARD" is a sound proxy for "served hosted".
|
|
1640
|
+
*
|
|
1641
|
+
* A REAL method, not a forwarded one: Reflect.get in the trap returns it before
|
|
1642
|
+
* the forwarding branch is reached, so callers get this implementation rather
|
|
1643
|
+
* than a forwarder that would POST "supportsMethod" to the edge.
|
|
1644
|
+
*/
|
|
1645
|
+
supportsMethod(name) {
|
|
1646
|
+
if (NO_FORWARD.has(name)) return false;
|
|
1647
|
+
if (name in _ProxyPapiAdapter.prototype) return true;
|
|
1648
|
+
return HOSTED_SERVED.has(name);
|
|
1649
|
+
}
|
|
1459
1650
|
/**
|
|
1460
1651
|
* task-1773: bearer-only auth probe. Hits the USER-scoped `project-list` route
|
|
1461
1652
|
* (no projectId needed), so it answers exactly one question: does the proxy
|
|
@@ -6193,7 +6384,78 @@ function validateFindings(findings) {
|
|
|
6193
6384
|
return { ok: violations.length === 0, violations };
|
|
6194
6385
|
}
|
|
6195
6386
|
var WAB_WINDOW_DAYS = 7;
|
|
6387
|
+
var WAB_DEFAULT_WEEKS = 8;
|
|
6196
6388
|
var WAB_WEEK_MS = WAB_WINDOW_DAYS * 24 * 60 * 60 * 1e3;
|
|
6389
|
+
function wabCompletionMs(row) {
|
|
6390
|
+
const ts = row.end_date ?? row.updated_at;
|
|
6391
|
+
if (!ts) return null;
|
|
6392
|
+
const ms = Date.parse(ts);
|
|
6393
|
+
return Number.isNaN(ms) ? null : ms;
|
|
6394
|
+
}
|
|
6395
|
+
function bucketWabSeries(rows, ownerUserId, nowMs, weeks = WAB_DEFAULT_WEEKS) {
|
|
6396
|
+
const buckets = Array.from({ length: weeks }, () => /* @__PURE__ */ new Set());
|
|
6397
|
+
for (const row of rows) {
|
|
6398
|
+
if (!row.user_id) continue;
|
|
6399
|
+
const ms = wabCompletionMs(row);
|
|
6400
|
+
if (ms === null) continue;
|
|
6401
|
+
const idx = Math.floor((nowMs - ms) / WAB_WEEK_MS);
|
|
6402
|
+
if (idx < 0 || idx >= weeks) continue;
|
|
6403
|
+
buckets[idx].add(row.user_id);
|
|
6404
|
+
}
|
|
6405
|
+
const out = [];
|
|
6406
|
+
for (let idx = weeks - 1; idx >= 0; idx--) {
|
|
6407
|
+
const owners = buckets[idx];
|
|
6408
|
+
const external = ownerUserId ? [...owners].filter((id) => id !== ownerUserId).length : owners.size;
|
|
6409
|
+
out.push({
|
|
6410
|
+
weekStart: new Date(nowMs - (idx + 1) * WAB_WEEK_MS).toISOString(),
|
|
6411
|
+
total: owners.size,
|
|
6412
|
+
external
|
|
6413
|
+
});
|
|
6414
|
+
}
|
|
6415
|
+
return out;
|
|
6416
|
+
}
|
|
6417
|
+
var PRODUCT_MARKER = /WHAT SHIPS FOR USERS\b[^\n:]*:/i;
|
|
6418
|
+
var MECHANICS_MARKER = /RELEASE MECHANICS\b[^\n:]*:/i;
|
|
6419
|
+
function splitCarryForward(raw) {
|
|
6420
|
+
const productMatch = PRODUCT_MARKER.exec(raw);
|
|
6421
|
+
const mechanicsMatch = MECHANICS_MARKER.exec(raw);
|
|
6422
|
+
if (!productMatch && !mechanicsMatch) {
|
|
6423
|
+
return { product: raw.trim(), mechanics: "", split: false };
|
|
6424
|
+
}
|
|
6425
|
+
if (!productMatch && mechanicsMatch) {
|
|
6426
|
+
const head = raw.slice(0, mechanicsMatch.index).trim();
|
|
6427
|
+
const tail = raw.slice(mechanicsMatch.index + mechanicsMatch[0].length).trim();
|
|
6428
|
+
return { product: head, mechanics: tail, split: true };
|
|
6429
|
+
}
|
|
6430
|
+
const productStart = productMatch.index + productMatch[0].length;
|
|
6431
|
+
if (!mechanicsMatch) {
|
|
6432
|
+
return { product: raw.slice(productStart).trim(), mechanics: "", split: true };
|
|
6433
|
+
}
|
|
6434
|
+
if (mechanicsMatch.index < productMatch.index) {
|
|
6435
|
+
return {
|
|
6436
|
+
product: raw.slice(productStart).trim(),
|
|
6437
|
+
mechanics: raw.slice(mechanicsMatch.index + mechanicsMatch[0].length, productMatch.index).trim(),
|
|
6438
|
+
split: true
|
|
6439
|
+
};
|
|
6440
|
+
}
|
|
6441
|
+
return {
|
|
6442
|
+
product: raw.slice(productStart, mechanicsMatch.index).trim(),
|
|
6443
|
+
mechanics: raw.slice(mechanicsMatch.index + mechanicsMatch[0].length).trim(),
|
|
6444
|
+
split: true
|
|
6445
|
+
};
|
|
6446
|
+
}
|
|
6447
|
+
function renderCarryForward(raw, decorate = (t) => t) {
|
|
6448
|
+
const { product, mechanics, split } = splitCarryForward(raw);
|
|
6449
|
+
if (!split) return [decorate(product)];
|
|
6450
|
+
const lines = [];
|
|
6451
|
+
if (product) lines.push(decorate(product));
|
|
6452
|
+
if (mechanics) {
|
|
6453
|
+
if (product) lines.push("");
|
|
6454
|
+
lines.push("**Release mechanics** \u2014 needed at release, not now:");
|
|
6455
|
+
lines.push(decorate(mechanics));
|
|
6456
|
+
}
|
|
6457
|
+
return lines;
|
|
6458
|
+
}
|
|
6197
6459
|
var CHECK_VALUE_MAX = 200;
|
|
6198
6460
|
function codeSpanSafe(value) {
|
|
6199
6461
|
const flat = value.replace(/[`\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -9860,6 +10122,13 @@ function formatBlockerWaiting(blocker) {
|
|
|
9860
10122
|
}
|
|
9861
10123
|
}
|
|
9862
10124
|
|
|
10125
|
+
// src/lib/adapter-capability.ts
|
|
10126
|
+
function adapterSupports(adapter2, method) {
|
|
10127
|
+
const declared = adapter2.supportsMethod;
|
|
10128
|
+
if (typeof declared === "function") return declared.call(adapter2, method);
|
|
10129
|
+
return typeof adapter2[method] === "function";
|
|
10130
|
+
}
|
|
10131
|
+
|
|
9863
10132
|
// src/lib/tool-telemetry.ts
|
|
9864
10133
|
var INSTRUMENTED_TOOLS = /* @__PURE__ */ new Set([
|
|
9865
10134
|
"plan",
|
|
@@ -9882,7 +10151,7 @@ function measureResultBytes(content) {
|
|
|
9882
10151
|
}
|
|
9883
10152
|
function recordToolRun(adapter2, sample) {
|
|
9884
10153
|
if (sample.toolName !== PLAN_PREPARE_TOOL_NAME && !WRAPPER_INSTRUMENTED_TOOLS.has(sample.toolName)) return;
|
|
9885
|
-
if (
|
|
10154
|
+
if (!adapterSupports(adapter2, "insertToolRun")) return;
|
|
9886
10155
|
try {
|
|
9887
10156
|
const promise = adapter2.insertToolRun({
|
|
9888
10157
|
toolName: sample.toolName,
|
|
@@ -9956,7 +10225,7 @@ function isProjectOwner(callerUserId, ownerUserId) {
|
|
|
9956
10225
|
return caller === owner;
|
|
9957
10226
|
}
|
|
9958
10227
|
async function resolveOwnerGate(adapter2, config2) {
|
|
9959
|
-
if (
|
|
10228
|
+
if (adapterSupports(adapter2, "getOwnerIdentity")) {
|
|
9960
10229
|
try {
|
|
9961
10230
|
const identity = await adapter2.getOwnerIdentity();
|
|
9962
10231
|
const callerUserId = identity.callerUserId ?? config2.userId ?? null;
|
|
@@ -9978,7 +10247,7 @@ async function resolveOwnerGate(adapter2, config2) {
|
|
|
9978
10247
|
};
|
|
9979
10248
|
}
|
|
9980
10249
|
}
|
|
9981
|
-
if (
|
|
10250
|
+
if (adapterSupports(adapter2, "getProjectOwnerUserId")) {
|
|
9982
10251
|
let ownerUserId = null;
|
|
9983
10252
|
let resolutionError;
|
|
9984
10253
|
try {
|
|
@@ -18897,7 +19166,7 @@ function isDecisionPending(task) {
|
|
|
18897
19166
|
return d != null && d.answer == null;
|
|
18898
19167
|
}
|
|
18899
19168
|
async function countFailedAttempts(adapter2, taskId) {
|
|
18900
|
-
if (
|
|
19169
|
+
if (!adapterSupports(adapter2, "getFailedBuildAttemptsForTask")) return null;
|
|
18901
19170
|
try {
|
|
18902
19171
|
return await adapter2.getFailedBuildAttemptsForTask(taskId);
|
|
18903
19172
|
} catch {
|
|
@@ -19310,7 +19579,7 @@ To override, pass force=true (emits a telemetry warning).`
|
|
|
19310
19579
|
boardHealth: existing?.boardHealth ?? "",
|
|
19311
19580
|
taskIds: existing?.taskIds ?? []
|
|
19312
19581
|
};
|
|
19313
|
-
if (
|
|
19582
|
+
if (adapterSupports(adapter2, "commitRelease")) {
|
|
19314
19583
|
try {
|
|
19315
19584
|
await adapter2.commitRelease({ cycle: completedCycle, snapshot: snapshot ?? null });
|
|
19316
19585
|
} catch (err) {
|
|
@@ -20868,7 +21137,7 @@ async function listBuilds(adapter2, config2) {
|
|
|
20868
21137
|
const [{ result: tasks, warnings }, health] = await Promise.all([
|
|
20869
21138
|
withBaseBranchSync(
|
|
20870
21139
|
{ projectRoot: config2.projectRoot, baseBranch: config2.baseBranch },
|
|
20871
|
-
() => adapter2.queryBoard()
|
|
21140
|
+
() => adapter2.queryBoard({ compact: true })
|
|
20872
21141
|
),
|
|
20873
21142
|
adapter2.getCycleHealth().catch(() => null)
|
|
20874
21143
|
]);
|
|
@@ -20876,14 +21145,16 @@ async function listBuilds(adapter2, config2) {
|
|
|
20876
21145
|
if (tasks.length === 0) {
|
|
20877
21146
|
return { sorted: [], inProgress: [], backlog: [], blocked: [], warnings, isEmpty: true, noHandoffs: false, currentCycle, totalTasks: tasks.length };
|
|
20878
21147
|
}
|
|
20879
|
-
const
|
|
20880
|
-
if (!t.buildHandoff) return false;
|
|
21148
|
+
const candidates = tasks.filter((t) => {
|
|
20881
21149
|
if (t.status === "Done" || t.status === "Cancelled" || t.status === "Blocked" || t.status === "Backlog" || t.status === "Deferred" || t.status === "Archived") {
|
|
20882
21150
|
return false;
|
|
20883
21151
|
}
|
|
20884
21152
|
if (currentCycle > 0 && t.cycle !== currentCycle) return false;
|
|
20885
21153
|
return true;
|
|
20886
21154
|
});
|
|
21155
|
+
const hydrated = candidates.length > 0 ? await adapter2.getTasks(candidates.map((t) => t.id)) : [];
|
|
21156
|
+
const byId = new Map(hydrated.map((t) => [t.id, t]));
|
|
21157
|
+
const withHandoff = candidates.map((t) => byId.get(t.id)).filter((t) => Boolean(t?.buildHandoff));
|
|
20887
21158
|
if (withHandoff.length === 0) {
|
|
20888
21159
|
return { sorted: [], inProgress: [], backlog: [], blocked: [], warnings, isEmpty: false, noHandoffs: true, currentCycle, totalTasks: tasks.length };
|
|
20889
21160
|
}
|
|
@@ -21381,7 +21652,7 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
21381
21652
|
assertDeployVerification(config2, input, { deployingNow: options.light === true });
|
|
21382
21653
|
const [healthResult, priorCount] = await Promise.all([
|
|
21383
21654
|
adapter2.getCycleHealth().catch(() => ({ totalCycles: 0 })),
|
|
21384
|
-
|
|
21655
|
+
adapterSupports(adapter2, "getBuildReportCountForTask") ? adapter2.getBuildReportCountForTask(taskId).catch(() => 0) : Promise.resolve(0)
|
|
21385
21656
|
]);
|
|
21386
21657
|
const cycleNumber = healthResult.totalCycles;
|
|
21387
21658
|
const iterationCount = priorCount + 1;
|
|
@@ -21425,7 +21696,7 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
21425
21696
|
console.error(
|
|
21426
21697
|
`[build] task ${taskId} related_decisions: ${relatedAdIds.length} AD(s) (${relatedAdIds.length === 0 ? "none" : relatedInferred ? "inferred from handoff" : "builder-provided"}).`
|
|
21427
21698
|
);
|
|
21428
|
-
if (report.startedAt && report.completedAt &&
|
|
21699
|
+
if (report.startedAt && report.completedAt && adapterSupports(adapter2, "getToolCallCount")) {
|
|
21429
21700
|
try {
|
|
21430
21701
|
const count = await adapter2.getToolCallCount(report.startedAt, report.completedAt);
|
|
21431
21702
|
if (count > 0) report.toolCallCount = count;
|
|
@@ -21438,7 +21709,7 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
21438
21709
|
const buildReportSummary = `${capitalizeCompleted(input.completed)}. Effort ${input.effort} vs estimated ${input.estimatedEffort}.${iterNote}${surpriseNote}${issueNote}`;
|
|
21439
21710
|
const statusChange = input.completed === "yes" ? { from: task.status, to: options.light ? "Done" : "In Review" } : void 0;
|
|
21440
21711
|
let atomicCommitDone = false;
|
|
21441
|
-
if (
|
|
21712
|
+
if (adapterSupports(adapter2, "commitBuildComplete")) {
|
|
21442
21713
|
await adapter2.commitBuildComplete({
|
|
21443
21714
|
report,
|
|
21444
21715
|
taskId,
|
|
@@ -21451,7 +21722,7 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
21451
21722
|
await adapter2.appendBuildReport(report);
|
|
21452
21723
|
}
|
|
21453
21724
|
let reportWriteVerified;
|
|
21454
|
-
if (
|
|
21725
|
+
if (adapterSupports(adapter2, "getBuildReportCountForTask")) {
|
|
21455
21726
|
try {
|
|
21456
21727
|
const postWriteCount = await adapter2.getBuildReportCountForTask(taskId);
|
|
21457
21728
|
reportWriteVerified = postWriteCount >= iterationCount;
|
|
@@ -21569,7 +21840,7 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
21569
21840
|
if (row.findingKey) insertedByKey.set(row.findingKey, row.inserted);
|
|
21570
21841
|
}
|
|
21571
21842
|
const haveInsertSignal = appendedRows.length > 0;
|
|
21572
|
-
if (findingRows.length > 0 &&
|
|
21843
|
+
if (findingRows.length > 0 && adapterSupports(adapter2, "createTask")) {
|
|
21573
21844
|
for (const { finding, learning } of findingRows) {
|
|
21574
21845
|
if (learning.category !== "issue") continue;
|
|
21575
21846
|
if (finding.disposition !== "filed") continue;
|
|
@@ -22390,7 +22661,7 @@ var MAX_DOC_BODY_BYTES = 2 * 1024 * 1024;
|
|
|
22390
22661
|
var PAID_TIERS = /* @__PURE__ */ new Set(["pro", "team"]);
|
|
22391
22662
|
var PRICING_URL = "https://getpapi.ai/pricing";
|
|
22392
22663
|
async function resolveTier(adapter2) {
|
|
22393
|
-
if (
|
|
22664
|
+
if (!adapterSupports(adapter2, "getMeteredUsage")) return null;
|
|
22394
22665
|
try {
|
|
22395
22666
|
const usage = await adapter2.getMeteredUsage();
|
|
22396
22667
|
return usage?.tier ?? null;
|
|
@@ -22405,7 +22676,7 @@ async function enforceProjectCap(adapter2, target) {
|
|
|
22405
22676
|
if (isSelfHostedDeployment(process.env.PAPI_SELF_HOST)) return null;
|
|
22406
22677
|
const tier = await resolveTier(adapter2);
|
|
22407
22678
|
if (tier === null || isPaidTier(tier)) return null;
|
|
22408
|
-
if (
|
|
22679
|
+
if (!adapterSupports(adapter2, "listUserProjects")) return null;
|
|
22409
22680
|
let projects;
|
|
22410
22681
|
try {
|
|
22411
22682
|
projects = await adapter2.listUserProjects();
|
|
@@ -22448,7 +22719,7 @@ The registry entry was saved and everything else is unaffected. Split the docume
|
|
|
22448
22719
|
const tier = await resolveTier(adapter2);
|
|
22449
22720
|
if (tier === null) return { storeBody: true };
|
|
22450
22721
|
const ceiling = DOC_STORAGE_CEILING_BY_TIER[tier] ?? DOC_STORAGE_CEILING_BY_TIER.free;
|
|
22451
|
-
if (
|
|
22722
|
+
if (!adapterSupports(adapter2, "getDocBodyUsage")) return { storeBody: true };
|
|
22452
22723
|
let usage;
|
|
22453
22724
|
try {
|
|
22454
22725
|
usage = await adapter2.getDocBodyUsage();
|
|
@@ -22625,6 +22896,7 @@ The registry stores metadata and a summary \u2014 not the body. This doc has one
|
|
|
22625
22896
|
);
|
|
22626
22897
|
}
|
|
22627
22898
|
if (isPathTracked(projectRoot, path7)) return "";
|
|
22899
|
+
if (isPathCommittedAnywhere(projectRoot, path7)) return "";
|
|
22628
22900
|
if (isPathIgnored(projectRoot, path7)) {
|
|
22629
22901
|
return warn(
|
|
22630
22902
|
`\`${path7}\` is excluded by .gitignore, so it cannot be committed (docs/private/ is ignored by design).`,
|
|
@@ -22826,7 +23098,7 @@ function resolveRestoreTarget(projectRoot, docPath) {
|
|
|
22826
23098
|
return { abs };
|
|
22827
23099
|
}
|
|
22828
23100
|
async function handleDocRead(adapter2, config2, args) {
|
|
22829
|
-
if (!adapter2.getDoc ||
|
|
23101
|
+
if (!adapter2.getDoc || !adapterSupports(adapter2, "getDocBody")) {
|
|
22830
23102
|
return errorResponse(
|
|
22831
23103
|
"Doc bodies are not available on this adapter \u2014 requires the pg/proxy adapter. Nothing is blocked; the file on disk (if any) is still the copy you have."
|
|
22832
23104
|
);
|
|
@@ -23985,7 +24257,7 @@ ${checklist}
|
|
|
23985
24257
|
});
|
|
23986
24258
|
}
|
|
23987
24259
|
let fixedResolvedCount = 0;
|
|
23988
|
-
if (fixedIssues && fixedIssues.length > 0 &&
|
|
24260
|
+
if (fixedIssues && fixedIssues.length > 0 && adapterSupports(adapter2, "markCycleLearningResolved")) {
|
|
23989
24261
|
const resolvedBy = `build:${result.task.displayId ?? taskId}`;
|
|
23990
24262
|
for (const learningId of fixedIssues) {
|
|
23991
24263
|
try {
|
|
@@ -26194,7 +26466,7 @@ Re-run build_execute complete with a production_verification field, then re-subm
|
|
|
26194
26466
|
}
|
|
26195
26467
|
const newStatus = resolveStatus(input.stage, input.verdict);
|
|
26196
26468
|
const statusChange = newStatus ? { from: task.status, to: newStatus } : void 0;
|
|
26197
|
-
if (
|
|
26469
|
+
if (adapterSupports(adapter2, "commitReviewSubmit")) {
|
|
26198
26470
|
await adapter2.commitReviewSubmit({
|
|
26199
26471
|
review,
|
|
26200
26472
|
taskId: input.taskId,
|
|
@@ -26225,7 +26497,7 @@ Re-run build_execute complete with a production_verification field, then re-subm
|
|
|
26225
26497
|
}
|
|
26226
26498
|
}
|
|
26227
26499
|
const closedDocActions = [];
|
|
26228
|
-
if (newStatus === "Done" &&
|
|
26500
|
+
if (newStatus === "Done" && adapterSupports(adapter2, "findPendingDocActionsForTask") && adapterSupports(adapter2, "updateDocAction")) {
|
|
26229
26501
|
try {
|
|
26230
26502
|
const pending = await adapter2.findPendingDocActionsForTask(input.taskId);
|
|
26231
26503
|
for (const match of pending) {
|
|
@@ -26236,7 +26508,7 @@ Re-run build_execute complete with a production_verification field, then re-subm
|
|
|
26236
26508
|
console.error(`[doc-closure] failed to resolve doc action ${match.docId}#${match.actionIndex}: ${err instanceof Error ? err.message : String(err)}`);
|
|
26237
26509
|
}
|
|
26238
26510
|
}
|
|
26239
|
-
if (closedDocActions.length > 0 &&
|
|
26511
|
+
if (closedDocActions.length > 0 && adapterSupports(adapter2, "appendCycleLearnings")) {
|
|
26240
26512
|
try {
|
|
26241
26513
|
await adapter2.appendCycleLearnings([{
|
|
26242
26514
|
taskId: input.taskId,
|
|
@@ -26497,6 +26769,19 @@ function mergeAfterAccept(config2, taskId) {
|
|
|
26497
26769
|
};
|
|
26498
26770
|
}
|
|
26499
26771
|
const details = [];
|
|
26772
|
+
if (isBranchMergedInto(config2.projectRoot, featureBranch, baseBranch)) {
|
|
26773
|
+
details.push(`Branch '${featureBranch}' is already merged into '${baseBranch}'.`);
|
|
26774
|
+
return { merged: true, skipped: false, message: `Already merged: \`${featureBranch}\` \u2192 \`${baseBranch}\`.`, details };
|
|
26775
|
+
}
|
|
26776
|
+
if (isBranchContentAlreadyInBase(config2.projectRoot, featureBranch, baseBranch)) {
|
|
26777
|
+
details.push(`Branch '${featureBranch}' adds nothing to '${baseBranch}' \u2014 its commits already landed there.`);
|
|
26778
|
+
return {
|
|
26779
|
+
merged: true,
|
|
26780
|
+
skipped: false,
|
|
26781
|
+
message: `Already on \`${baseBranch}\` \u2014 no PR merge needed (these commits were folded into another branch and landed with it).`,
|
|
26782
|
+
details
|
|
26783
|
+
};
|
|
26784
|
+
}
|
|
26500
26785
|
const papiDir = join15(config2.projectRoot, ".papi");
|
|
26501
26786
|
if (existsSync10(papiDir)) {
|
|
26502
26787
|
try {
|
|
@@ -26841,7 +27126,7 @@ ${overlap}`;
|
|
|
26841
27126
|
\u2705 Verdict recorded. All cycle tasks are Done, but **auto-release is owner-only** \u2014 your identity does not match this project's owner, so no release was cut. Push your branch and open a PR for the owner to run \`release\`.${resolutionNote}`;
|
|
26842
27127
|
} else if (cycleTasks.length > 0 && cycleTasks.every((t) => t.status === "Done")) {
|
|
26843
27128
|
let planRunCount = null;
|
|
26844
|
-
if (
|
|
27129
|
+
if (adapterSupports(adapter2, "countPlanRunsForCycle")) {
|
|
26845
27130
|
try {
|
|
26846
27131
|
planRunCount = await adapter2.countPlanRunsForCycle(result.currentCycle);
|
|
26847
27132
|
} catch {
|
|
@@ -27044,7 +27329,7 @@ async function resolveReviewerIdentity(adapter2, config2) {
|
|
|
27044
27329
|
const note = gate.resolutionError ? ` (${gate.resolutionError})` : "";
|
|
27045
27330
|
return { error: "Reviewing requires a resolvable user identity, but none was found" + note + ". Set PAPI_USER_ID to your account UUID (local) \u2014 hosted sessions derive it from the bearer token." };
|
|
27046
27331
|
}
|
|
27047
|
-
if (!gate.callerIsOwner &&
|
|
27332
|
+
if (!gate.callerIsOwner && adapterSupports(adapter2, "listContributors")) {
|
|
27048
27333
|
try {
|
|
27049
27334
|
const members = await adapter2.listContributors();
|
|
27050
27335
|
if (!members.some((m) => m.userId === callerUserId)) {
|
|
@@ -27059,7 +27344,7 @@ async function resolveReviewerIdentity(adapter2, config2) {
|
|
|
27059
27344
|
async function handleReviewClaim(adapter2, config2, args) {
|
|
27060
27345
|
const taskId = typeof args.task_id === "string" ? args.task_id.trim() : "";
|
|
27061
27346
|
if (!taskId) return errorResponse('A task_id is required. Example: review_claim task_id="task-2072"');
|
|
27062
|
-
if (
|
|
27347
|
+
if (!adapterSupports(adapter2, "claimReview")) {
|
|
27063
27348
|
return errorResponse("The shared review queue is not available on this adapter.");
|
|
27064
27349
|
}
|
|
27065
27350
|
const identity = await resolveReviewerIdentity(adapter2, config2);
|
|
@@ -28008,50 +28293,6 @@ function formatUnblockSection(candidates) {
|
|
|
28008
28293
|
return lines.join("\n");
|
|
28009
28294
|
}
|
|
28010
28295
|
|
|
28011
|
-
// src/lib/carry-forward-shape.ts
|
|
28012
|
-
var PRODUCT_MARKER = /WHAT SHIPS FOR USERS\b[^\n:]*:/i;
|
|
28013
|
-
var MECHANICS_MARKER = /RELEASE MECHANICS\b[^\n:]*:/i;
|
|
28014
|
-
function splitCarryForward(raw) {
|
|
28015
|
-
const productMatch = PRODUCT_MARKER.exec(raw);
|
|
28016
|
-
const mechanicsMatch = MECHANICS_MARKER.exec(raw);
|
|
28017
|
-
if (!productMatch && !mechanicsMatch) {
|
|
28018
|
-
return { product: raw.trim(), mechanics: "", split: false };
|
|
28019
|
-
}
|
|
28020
|
-
if (!productMatch && mechanicsMatch) {
|
|
28021
|
-
const head = raw.slice(0, mechanicsMatch.index).trim();
|
|
28022
|
-
const tail = raw.slice(mechanicsMatch.index + mechanicsMatch[0].length).trim();
|
|
28023
|
-
return { product: head, mechanics: tail, split: true };
|
|
28024
|
-
}
|
|
28025
|
-
const productStart = productMatch.index + productMatch[0].length;
|
|
28026
|
-
if (!mechanicsMatch) {
|
|
28027
|
-
return { product: raw.slice(productStart).trim(), mechanics: "", split: true };
|
|
28028
|
-
}
|
|
28029
|
-
if (mechanicsMatch.index < productMatch.index) {
|
|
28030
|
-
return {
|
|
28031
|
-
product: raw.slice(productStart).trim(),
|
|
28032
|
-
mechanics: raw.slice(mechanicsMatch.index + mechanicsMatch[0].length, productMatch.index).trim(),
|
|
28033
|
-
split: true
|
|
28034
|
-
};
|
|
28035
|
-
}
|
|
28036
|
-
return {
|
|
28037
|
-
product: raw.slice(productStart, mechanicsMatch.index).trim(),
|
|
28038
|
-
mechanics: raw.slice(mechanicsMatch.index + mechanicsMatch[0].length).trim(),
|
|
28039
|
-
split: true
|
|
28040
|
-
};
|
|
28041
|
-
}
|
|
28042
|
-
function renderCarryForward(raw, decorate = (t) => t) {
|
|
28043
|
-
const { product, mechanics, split } = splitCarryForward(raw);
|
|
28044
|
-
if (!split) return [decorate(product)];
|
|
28045
|
-
const lines = [];
|
|
28046
|
-
if (product) lines.push(decorate(product));
|
|
28047
|
-
if (mechanics) {
|
|
28048
|
-
if (product) lines.push("");
|
|
28049
|
-
lines.push("**Release mechanics** \u2014 needed at release, not now:");
|
|
28050
|
-
lines.push(decorate(mechanics));
|
|
28051
|
-
}
|
|
28052
|
-
return lines;
|
|
28053
|
-
}
|
|
28054
|
-
|
|
28055
28296
|
// src/lib/deferred-gate.ts
|
|
28056
28297
|
var GATE_PHRASES = [
|
|
28057
28298
|
"depends on",
|
|
@@ -28372,12 +28613,39 @@ var evaluateSelfServeActivation = async (adapter2, projectId) => {
|
|
|
28372
28613
|
unevaluatedReason: `mechanically satisfied (${users} non-owner accounts planned in their own project) \u2014 but "ZERO owner intervention" is a human judgement, so this needs an owner tick via set_criterion_met`
|
|
28373
28614
|
} : { met: false, evidence: `0 non-owner accounts with a planned cycle. Counted ${(/* @__PURE__ */ new Date()).toISOString()}.` };
|
|
28374
28615
|
};
|
|
28616
|
+
var evaluateWeeklyActiveBuilders = async (adapter2, projectId) => {
|
|
28617
|
+
const sql = sqlOf(adapter2);
|
|
28618
|
+
if (!sql) return { met: null, unevaluatedReason: "no direct SQL access on this adapter (hosted path)" };
|
|
28619
|
+
const owner = await sql`
|
|
28620
|
+
SELECT user_id FROM projects WHERE id = ${projectId}
|
|
28621
|
+
`;
|
|
28622
|
+
const ownerUserId = owner[0]?.user_id ?? null;
|
|
28623
|
+
const rows = await sql`
|
|
28624
|
+
SELECT c.user_id, c.end_date, c.updated_at
|
|
28625
|
+
FROM cycles c
|
|
28626
|
+
JOIN projects p ON p.id = c.project_id
|
|
28627
|
+
JOIN auth.users u ON u.id = p.user_id
|
|
28628
|
+
WHERE c.status = 'complete'
|
|
28629
|
+
AND c.user_id IS NOT NULL
|
|
28630
|
+
AND u.email NOT LIKE ${TEST_ACCOUNT_PATTERN}
|
|
28631
|
+
`;
|
|
28632
|
+
const series = bucketWabSeries(rows, ownerUserId, Date.now(), WAB_DEFAULT_WEEKS);
|
|
28633
|
+
const peak = series.reduce((max, w) => Math.max(max, w.external), 0);
|
|
28634
|
+
let consecutive = 0;
|
|
28635
|
+
let bestRun = 0;
|
|
28636
|
+
for (const week of series) {
|
|
28637
|
+
consecutive = week.external >= 3 ? consecutive + 1 : 0;
|
|
28638
|
+
bestRun = Math.max(bestRun, consecutive);
|
|
28639
|
+
}
|
|
28640
|
+
const counted = `Peak ${peak} external builder(s) in any trailing week; longest run at >=3 is ${bestRun} consecutive week(s) over the last ${WAB_DEFAULT_WEEKS}. Owner and ${TEST_ACCOUNT_PATTERN} excluded. Counted ${(/* @__PURE__ */ new Date()).toISOString()}.`;
|
|
28641
|
+
return bestRun >= 2 ? { met: true, evidence: counted } : { met: false, evidence: counted };
|
|
28642
|
+
};
|
|
28375
28643
|
var EVALUATORS = {
|
|
28376
28644
|
"c4a11d42-b222-45af-a470-08970e1bd6a9": evaluateSelfServeActivation,
|
|
28377
|
-
"59751bcb-f719-4cf6-a4d4-608770999a22": evaluateUnaidedFullLoop
|
|
28645
|
+
"59751bcb-f719-4cf6-a4d4-608770999a22": evaluateUnaidedFullLoop,
|
|
28646
|
+
"e9d984f4-1e75-4486-8605-5dfd07412915": evaluateWeeklyActiveBuilders
|
|
28378
28647
|
};
|
|
28379
28648
|
var UNEVALUATABLE = {
|
|
28380
|
-
"e9d984f4-1e75-4486-8605-5dfd07412915": "no evaluator here \u2014 the canonical completion-keyed definition lives in lib/weekly-active-builders.ts (Next app) and must not be re-derived in the server",
|
|
28381
28649
|
"c596a082-9ad3-4089-b497-46cd189e892b": "threshold is provisional pending an owner ruling \u2014 no settled bar to evaluate against"
|
|
28382
28650
|
};
|
|
28383
28651
|
async function evaluateExitCriteria(adapter2, projectId, criteria) {
|
|
@@ -28787,7 +29055,10 @@ async function getHierarchyPosition(adapter2, projectId) {
|
|
|
28787
29055
|
adapter2.readHorizons?.() ?? [],
|
|
28788
29056
|
adapter2.readStages?.() ?? [],
|
|
28789
29057
|
adapter2.readPhases(),
|
|
28790
|
-
|
|
29058
|
+
// Compact: the only fields read below are `phase` and `status`, for the
|
|
29059
|
+
// per-phase done/total tally. A non-compact pull dragged every handoff and
|
|
29060
|
+
// build report across the wire for two columns.
|
|
29061
|
+
adapter2.queryBoard({ compact: true })
|
|
28791
29062
|
]);
|
|
28792
29063
|
if (horizons.length === 0) return void 0;
|
|
28793
29064
|
const isInProgress = (s) => s.status === "In Progress";
|
|
@@ -28907,7 +29178,7 @@ function formatDiscoveredIssuesBlocks(candidateLearnings, closedTaskIds) {
|
|
|
28907
29178
|
return { alertsNote, unactionedIssuesNote };
|
|
28908
29179
|
}
|
|
28909
29180
|
async function computeTeamSummary(adapter2, contributorsInput) {
|
|
28910
|
-
if (
|
|
29181
|
+
if (!adapterSupports(adapter2, "listContributors")) return void 0;
|
|
28911
29182
|
let members;
|
|
28912
29183
|
try {
|
|
28913
29184
|
members = (await (contributorsInput ?? adapter2.listContributors())).length;
|
|
@@ -28928,7 +29199,7 @@ async function computeTeamSummary(adapter2, contributorsInput) {
|
|
|
28928
29199
|
return `**Team:** ${members} members \xB7 ${pool} in pool \xB7 ${inFlight} in flight \xB7 ${reviewQueue} in review`;
|
|
28929
29200
|
}
|
|
28930
29201
|
async function computeReleaseHistory(adapter2, contributorsInput) {
|
|
28931
|
-
if (
|
|
29202
|
+
if (!adapterSupports(adapter2, "listContributors")) return void 0;
|
|
28932
29203
|
let contributors;
|
|
28933
29204
|
try {
|
|
28934
29205
|
contributors = await (contributorsInput ?? adapter2.listContributors());
|
|
@@ -28968,7 +29239,29 @@ function formatResolvedFeedback(items) {
|
|
|
28968
29239
|
lines.push("_Thanks for the signal \u2014 it shaped what shipped._");
|
|
28969
29240
|
return lines.join("\n");
|
|
28970
29241
|
}
|
|
28971
|
-
|
|
29242
|
+
function withBoardMemo(adapter2) {
|
|
29243
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
29244
|
+
const queryBoard = (options) => {
|
|
29245
|
+
const key = JSON.stringify(options ?? null);
|
|
29246
|
+
const hit = inflight.get(key);
|
|
29247
|
+
if (hit) return hit;
|
|
29248
|
+
const pending = adapter2.queryBoard(options).catch((err) => {
|
|
29249
|
+
inflight.delete(key);
|
|
29250
|
+
throw err;
|
|
29251
|
+
});
|
|
29252
|
+
inflight.set(key, pending);
|
|
29253
|
+
return pending;
|
|
29254
|
+
};
|
|
29255
|
+
return new Proxy(adapter2, {
|
|
29256
|
+
get(target, prop, receiver) {
|
|
29257
|
+
if (prop === "queryBoard") return queryBoard;
|
|
29258
|
+
const value = Reflect.get(target, prop, receiver);
|
|
29259
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
29260
|
+
}
|
|
29261
|
+
});
|
|
29262
|
+
}
|
|
29263
|
+
async function handleOrient(rawAdapter, config2, args = {}, clientName) {
|
|
29264
|
+
const adapter2 = withBoardMemo(rawAdapter);
|
|
28972
29265
|
const environment = normaliseEnvironment(args.environment);
|
|
28973
29266
|
const deepHousekeeping = args.deep_housekeeping === true;
|
|
28974
29267
|
const fullEnrichment = args.full === true || deepHousekeeping;
|
|
@@ -29464,7 +29757,7 @@ ${versionDrift}` : "";
|
|
|
29464
29757
|
}
|
|
29465
29758
|
}
|
|
29466
29759
|
tracker.mark("parallel-tail");
|
|
29467
|
-
const sharedContributorsPromise =
|
|
29760
|
+
const sharedContributorsPromise = adapterSupports(adapter2, "listContributors") ? adapter2.listContributors().catch(() => []) : Promise.resolve([]);
|
|
29468
29761
|
const [unblockCandidates, subAgents, teamSummaryLine, releaseHistoryLine, carryForwardRefs] = await Promise.all([
|
|
29469
29762
|
tracked("unblock-candidates", () => findUnblockCandidates(adapter2, currentCycle))().catch(() => []),
|
|
29470
29763
|
// task-1866: discover project sub-agents for the orient surface (read-only, never throws).
|
|
@@ -31071,7 +31364,7 @@ var contributorListTool = {
|
|
|
31071
31364
|
}
|
|
31072
31365
|
};
|
|
31073
31366
|
function denyUnlessCohortCapable(adapter2) {
|
|
31074
|
-
if (
|
|
31367
|
+
if (!adapterSupports(adapter2, "listContributors")) {
|
|
31075
31368
|
return "Contributor management is not available on this adapter (requires the pg or proxy adapter \u2014 the local md adapter has no cohort).";
|
|
31076
31369
|
}
|
|
31077
31370
|
return null;
|
|
@@ -31092,7 +31385,7 @@ async function denyUnlessMember(adapter2, config2) {
|
|
|
31092
31385
|
const gate = await resolveOwnerGate(adapter2, config2);
|
|
31093
31386
|
if (!gate.enforced) return null;
|
|
31094
31387
|
if (gate.callerIsOwner) return null;
|
|
31095
|
-
const callerRole = gate.callerUserId &&
|
|
31388
|
+
const callerRole = gate.callerUserId && adapterSupports(adapter2, "getContributorRole") ? await adapter2.getContributorRole(gate.callerUserId).catch(() => null) : null;
|
|
31096
31389
|
if (callerRole) return null;
|
|
31097
31390
|
const note = gate.resolutionError ? ` (Identity resolution failed: ${gate.resolutionError} \u2014 the gate fails closed; retry once connectivity is restored.)` : "";
|
|
31098
31391
|
return `Listing contributors is restricted to project members. Your identity does not match this project's owner or any contributor.${note}`;
|
|
@@ -31193,7 +31486,7 @@ function requireTaskId(args) {
|
|
|
31193
31486
|
return id.length > 0 ? id : null;
|
|
31194
31487
|
}
|
|
31195
31488
|
async function resolveClaimerIdentity(adapter2, config2) {
|
|
31196
|
-
if (
|
|
31489
|
+
if (!adapterSupports(adapter2, "claimTask")) {
|
|
31197
31490
|
return { error: "Claiming is not available on this adapter." };
|
|
31198
31491
|
}
|
|
31199
31492
|
const gate = await resolveOwnerGate(adapter2, config2);
|
|
@@ -31204,7 +31497,7 @@ async function resolveClaimerIdentity(adapter2, config2) {
|
|
|
31204
31497
|
error: "Claiming requires a resolvable user identity, but none was found" + note + ". Set PAPI_USER_ID to your account UUID (local) \u2014 hosted sessions derive it from your bearer token."
|
|
31205
31498
|
};
|
|
31206
31499
|
}
|
|
31207
|
-
if (!gate.callerIsOwner &&
|
|
31500
|
+
if (!gate.callerIsOwner && adapterSupports(adapter2, "listContributors")) {
|
|
31208
31501
|
try {
|
|
31209
31502
|
const members = await adapter2.listContributors();
|
|
31210
31503
|
if (!members.some((m) => m.userId === callerUserId)) {
|
|
@@ -31290,7 +31583,7 @@ ${claimedList}`
|
|
|
31290
31583
|
async function handleTaskUnclaim(adapter2, config2, args) {
|
|
31291
31584
|
const taskId = requireTaskId(args);
|
|
31292
31585
|
if (!taskId) return errorResponse('A task_id is required. Example: task_unclaim task_id="task-2071"');
|
|
31293
|
-
if (
|
|
31586
|
+
if (!adapterSupports(adapter2, "unclaimTask")) {
|
|
31294
31587
|
return errorResponse("Unclaiming is not available on this adapter.");
|
|
31295
31588
|
}
|
|
31296
31589
|
const identity = await resolveClaimerIdentity(adapter2, config2);
|
|
@@ -31341,7 +31634,7 @@ async function handleTaskMove(adapter2, config2, args) {
|
|
|
31341
31634
|
if (!taskId) return errorResponse('A task_id is required. Example: task_move task_id="task-42" target_project="other-project" confirm=true');
|
|
31342
31635
|
const targetProject = requireStr(args.target_project);
|
|
31343
31636
|
if (!targetProject) return errorResponse("A target_project (slug or UUID) is required \u2014 the project you want to move the task into.");
|
|
31344
|
-
if (
|
|
31637
|
+
if (!adapterSupports(adapter2, "moveTask")) {
|
|
31345
31638
|
return errorResponse("Cross-project move is not available on this adapter.");
|
|
31346
31639
|
}
|
|
31347
31640
|
const gate = await resolveOwnerGate(adapter2, config2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papi-ai/server",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.82",
|
|
4
4
|
"description": "PAPI MCP server — AI-powered sprint planning, build execution, and strategy review for software projects",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"mcpName": "io.github.getpapi/papi",
|