@papi-ai/server 0.7.81 → 0.7.83
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 +369 -114
- package/dist/index.js +6562 -6024
- package/package.json +1 -1
|
@@ -8,6 +8,151 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// ../shared/dist/index.js
|
|
12
|
+
function isSelfHostedDeployment(value) {
|
|
13
|
+
return value === "1" || value?.toLowerCase() === "true";
|
|
14
|
+
}
|
|
15
|
+
function optionalBoolean(value, fallback) {
|
|
16
|
+
if (value == null || value.trim() === "") return fallback;
|
|
17
|
+
return isSelfHostedDeployment(value);
|
|
18
|
+
}
|
|
19
|
+
function cleanUrl(value) {
|
|
20
|
+
const cleaned = value?.trim().replace(/\/+$/, "");
|
|
21
|
+
return cleaned || void 0;
|
|
22
|
+
}
|
|
23
|
+
function resolveDeploymentProfile(env) {
|
|
24
|
+
const selfHosted = isSelfHostedDeployment(env.selfHosted);
|
|
25
|
+
const hostedSupabaseUrl = cleanUrl(env.hostedSupabaseUrl);
|
|
26
|
+
return {
|
|
27
|
+
kind: selfHosted ? "self-hosted" : "hosted",
|
|
28
|
+
appUrl: cleanUrl(env.appUrl) ?? (selfHosted ? void 0 : HOSTED_APP_URL),
|
|
29
|
+
mcpUrl: cleanUrl(env.mcpUrl) ?? (selfHosted ? void 0 : HOSTED_MCP_URL),
|
|
30
|
+
dataEndpoint: cleanUrl(env.dataEndpoint) ?? (hostedSupabaseUrl ? `${hostedSupabaseUrl}/functions/v1/data-proxy` : void 0) ?? (selfHosted ? void 0 : `${HOSTED_SUPABASE_URL}/functions/v1/data-proxy`),
|
|
31
|
+
providers: {
|
|
32
|
+
vercel: optionalBoolean(env.vercelEnabled, !selfHosted),
|
|
33
|
+
railway: optionalBoolean(env.railwayEnabled, !selfHosted),
|
|
34
|
+
managedSupabase: optionalBoolean(env.managedSupabaseEnabled, !selfHosted)
|
|
35
|
+
},
|
|
36
|
+
auth: {
|
|
37
|
+
emailPassword: optionalBoolean(env.emailPasswordEnabled, true),
|
|
38
|
+
emailSignup: optionalBoolean(env.emailSignupEnabled, false),
|
|
39
|
+
google: optionalBoolean(env.googleAuthEnabled, !selfHosted),
|
|
40
|
+
github: optionalBoolean(env.githubAuthEnabled, !selfHosted)
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function effortOrdinal(effort) {
|
|
45
|
+
if (typeof effort !== "string") return void 0;
|
|
46
|
+
const normalized = effort.trim().toUpperCase();
|
|
47
|
+
return EFFORT_SCALE[normalized];
|
|
48
|
+
}
|
|
49
|
+
function isUnparsedEffort(effort) {
|
|
50
|
+
if (typeof effort !== "string" || effort.trim().length === 0) return false;
|
|
51
|
+
return effortOrdinal(effort) === void 0;
|
|
52
|
+
}
|
|
53
|
+
function calculateCycleMetrics(reports, currentCycle, window = 5) {
|
|
54
|
+
const recentReports = reports.filter(
|
|
55
|
+
(r) => r.cycle > currentCycle - window && r.cycle <= currentCycle
|
|
56
|
+
);
|
|
57
|
+
const unparsedEffortCount = recentReports.filter(
|
|
58
|
+
(r) => isUnparsedEffort(r.actualEffort) || isUnparsedEffort(r.estimatedEffort)
|
|
59
|
+
).length;
|
|
60
|
+
const perCycle = /* @__PURE__ */ new Map();
|
|
61
|
+
for (const r of recentReports) {
|
|
62
|
+
const group = perCycle.get(r.cycle) ?? [];
|
|
63
|
+
group.push(r);
|
|
64
|
+
perCycle.set(r.cycle, group);
|
|
65
|
+
}
|
|
66
|
+
const accuracy = [];
|
|
67
|
+
const velocity = [];
|
|
68
|
+
const sortedCycles = [...perCycle.keys()].sort((a, b) => a - b);
|
|
69
|
+
for (const cycle of sortedCycles) {
|
|
70
|
+
const reps = perCycle.get(cycle);
|
|
71
|
+
const deltas = [];
|
|
72
|
+
for (const r of reps) {
|
|
73
|
+
const actual = effortOrdinal(r.actualEffort);
|
|
74
|
+
const estimated = effortOrdinal(r.estimatedEffort);
|
|
75
|
+
if (actual !== void 0 && estimated !== void 0) {
|
|
76
|
+
deltas.push(actual - estimated);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (deltas.length > 0) {
|
|
80
|
+
accuracy.push({
|
|
81
|
+
cycle,
|
|
82
|
+
reports: deltas.length,
|
|
83
|
+
matchRate: Math.round(deltas.filter((d) => d === 0).length / deltas.length * 100),
|
|
84
|
+
mae: Math.round(deltas.reduce((s, d) => s + Math.abs(d), 0) / deltas.length * 10) / 10,
|
|
85
|
+
bias: Math.round(deltas.reduce((s, d) => s + d, 0) / deltas.length * 10) / 10
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
velocity.push({
|
|
89
|
+
cycle,
|
|
90
|
+
completed: reps.filter((r) => r.completed === "Yes").length,
|
|
91
|
+
partial: reps.filter((r) => r.completed === "Partial").length,
|
|
92
|
+
failed: reps.filter((r) => r.completed === "No").length,
|
|
93
|
+
effortPoints: reps.reduce((s, r) => s + (effortOrdinal(r.actualEffort) ?? 0), 0)
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return { accuracy, velocity, unparsedEffortCount };
|
|
97
|
+
}
|
|
98
|
+
var CAPABILITY_REGISTRY, CAPABILITY_KEYS, HOSTED_APP_URL, HOSTED_MCP_URL, HOSTED_SUPABASE_URL, WAB_WINDOW_DAYS, WAB_WEEK_MS, EFFORT_SCALE;
|
|
99
|
+
var init_dist = __esm({
|
|
100
|
+
"../shared/dist/index.js"() {
|
|
101
|
+
"use strict";
|
|
102
|
+
CAPABILITY_REGISTRY = [
|
|
103
|
+
{ key: "prReviewer", label: "Auto code review", description: "Runs a code review of the branch diff before accepting.", step: "review" },
|
|
104
|
+
{ key: "securityScan", label: "Security scan", description: "Flags a security pass on risk-tier changes at review time.", step: "review" },
|
|
105
|
+
{ key: "changelog", label: "Changelog & cycle update", description: "Curates a cycle-update post when a release ships.", step: "release" },
|
|
106
|
+
{ key: "verifyHealthCheck", label: "Release health check", description: "Reminds you to verify cycle state before release.", step: "release" },
|
|
107
|
+
{ key: "gestaltPreBuild", label: "Gestalt pre-build check", description: "Reads the whole cycle before building the first task.", step: "build" },
|
|
108
|
+
{ key: "batchBuildRollup", label: "Batch build rollup", description: "Emits a cross-task summary after a batch build.", step: "build" },
|
|
109
|
+
{ key: "modelRecommendation", label: "Model recommendation", description: "Suggests a model tier for each task.", step: "build" },
|
|
110
|
+
{ key: "discoveredIssues", label: "Discovered issues surfacing", description: "Surfaces issues logged during builds at release.", step: "release" },
|
|
111
|
+
{ key: "publishDirective", label: "Publish & registry updates", description: "Announces the release and updates MCP registry listings.", step: "release" },
|
|
112
|
+
// task-2482 (C319): the release quality gate. When on AND a gate command is
|
|
113
|
+
// configured (PAPI_GATE), release makes the host LLM run that command and
|
|
114
|
+
// BLOCKS the tag/merge on failure (fail-closed, AD-58 — PAPI never runs it).
|
|
115
|
+
{ key: "releaseGate", label: "Release quality gate", description: "Runs your test/build command before release and blocks on failure.", step: "release" },
|
|
116
|
+
// task-2491 (C320): post-release deploy hook — the deploy half of the ship→verify
|
|
117
|
+
// loop. UNLIKE every other capability this one defaults OFF (defaultEnabled:false):
|
|
118
|
+
// running a deploy command is destructive, so it is strictly opt-in. When ON AND
|
|
119
|
+
// papi.deploy (PAPI_DEPLOY) is set, release emits a directive telling the HOST to
|
|
120
|
+
// run the deploy command AFTER the merge/tag (AD-58 — PAPI never runs it) and
|
|
121
|
+
// records a deploy_hook progress step so the reactive hub shows the deploy step.
|
|
122
|
+
{ key: "deployHook", label: "Post-release deploy", description: "Runs your deploy command after a release merges.", step: "release", defaultEnabled: false },
|
|
123
|
+
// task-2833 (C341): enforce the handoff's acceptanceCriteria[] at build-complete.
|
|
124
|
+
// When ON, a completed:"yes" build whose handoff lists acceptance criteria must
|
|
125
|
+
// pass acceptance_confirmed:true or build_execute returns the checklist and does
|
|
126
|
+
// NOT mark the task Done (non-destructive — the report is not discarded). Defaults
|
|
127
|
+
// OFF: it changes the completion contract, so it is strictly opt-in until a project
|
|
128
|
+
// chooses to hold builds to their own acceptance criteria.
|
|
129
|
+
{ key: "acceptanceGate", label: "Acceptance-criteria gate", description: "Requires confirming the handoff acceptance criteria before a build completes.", step: "build", defaultEnabled: false },
|
|
130
|
+
// task-2325 (C344): configurable workflow guardrails — let a user opt out of the
|
|
131
|
+
// git/branch/commit/PR ceremony and PAPI-meta framing so PAPI adapts to their
|
|
132
|
+
// workflow instead of imposing one. Every toggle DEFAULTS ON (no defaultEnabled)
|
|
133
|
+
// so current behaviour is byte-identical until the user flips it off. The no-git
|
|
134
|
+
// fallback that these ride on top of is a separate task (task-2353).
|
|
135
|
+
{ key: "autoBranch", label: "Auto branch", description: "Creates a feature branch per task/cycle at build start.", step: "build" },
|
|
136
|
+
{ key: "autoCommit", label: "Auto commit", description: "Commits your work automatically when a build completes.", step: "build" },
|
|
137
|
+
{ key: "autoPush", label: "Auto push & PR", description: "Pushes the branch and opens a pull request on build complete.", step: "build" },
|
|
138
|
+
{ key: "papiMetaFraming", label: "PAPI self-referential framing", description: "Includes PAPI-meta build-discipline framing in handoff output.", step: "build" }
|
|
139
|
+
];
|
|
140
|
+
CAPABILITY_KEYS = CAPABILITY_REGISTRY.map((c) => c.key);
|
|
141
|
+
HOSTED_APP_URL = "https://getpapi.ai";
|
|
142
|
+
HOSTED_MCP_URL = "https://mcp.getpapi.ai";
|
|
143
|
+
HOSTED_SUPABASE_URL = "https://guewgygcpcmrcoppihzx.supabase.co";
|
|
144
|
+
WAB_WINDOW_DAYS = 7;
|
|
145
|
+
WAB_WEEK_MS = WAB_WINDOW_DAYS * 24 * 60 * 60 * 1e3;
|
|
146
|
+
EFFORT_SCALE = {
|
|
147
|
+
XS: 1,
|
|
148
|
+
S: 2,
|
|
149
|
+
M: 3,
|
|
150
|
+
L: 4,
|
|
151
|
+
XL: 5
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
11
156
|
// src/lib/git.ts
|
|
12
157
|
var git_exports = {};
|
|
13
158
|
__export(git_exports, {
|
|
@@ -63,6 +208,7 @@ __export(git_exports, {
|
|
|
63
208
|
isGhAvailable: () => isGhAvailable,
|
|
64
209
|
isGitAvailable: () => isGitAvailable,
|
|
65
210
|
isGitRepo: () => isGitRepo,
|
|
211
|
+
isPathCommittedAnywhere: () => isPathCommittedAnywhere,
|
|
66
212
|
isPathIgnored: () => isPathIgnored,
|
|
67
213
|
isPathTracked: () => isPathTracked,
|
|
68
214
|
listGroupedCycleBranches: () => listGroupedCycleBranches,
|
|
@@ -102,6 +248,22 @@ function isGitRepo(cwd) {
|
|
|
102
248
|
return false;
|
|
103
249
|
}
|
|
104
250
|
}
|
|
251
|
+
function isPathCommittedAnywhere(cwd, path3) {
|
|
252
|
+
const run = (args) => {
|
|
253
|
+
try {
|
|
254
|
+
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
255
|
+
} catch {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const commit = run(["log", "--all", "--format=%H", "-1", "--", path3]);
|
|
260
|
+
if (!commit) return false;
|
|
261
|
+
const committedBlob = run(["rev-parse", `${commit}:${path3}`]);
|
|
262
|
+
if (!committedBlob) return false;
|
|
263
|
+
const onDiskBlob = run(["hash-object", "--", path3]);
|
|
264
|
+
if (!onDiskBlob) return false;
|
|
265
|
+
return committedBlob === onDiskBlob;
|
|
266
|
+
}
|
|
105
267
|
function isPathTracked(cwd, path3) {
|
|
106
268
|
try {
|
|
107
269
|
execFileSync("git", ["ls-files", "--error-unmatch", "--", path3], {
|
|
@@ -1213,14 +1375,12 @@ function reportTelemetryEmitFailure(reason, ctx) {
|
|
|
1213
1375
|
function noteTelemetryEmitSuccess() {
|
|
1214
1376
|
consecutiveTelemetryFailures = 0;
|
|
1215
1377
|
}
|
|
1216
|
-
var
|
|
1378
|
+
var consecutiveTelemetryFailures, TELEMETRY_BLACKOUT_THRESHOLD;
|
|
1217
1379
|
var init_telemetry = __esm({
|
|
1218
1380
|
"src/lib/telemetry.ts"() {
|
|
1219
1381
|
"use strict";
|
|
1220
1382
|
init_install_id();
|
|
1221
|
-
|
|
1222
|
-
DEFAULT_TELEMETRY_ENDPOINT = `${HOSTED_SUPABASE_URL}/functions/v1/data-proxy`;
|
|
1223
|
-
MD_PINGS_SUPABASE_URL = process.env["PAPI_MD_PINGS_URL"] ?? HOSTED_SUPABASE_URL;
|
|
1383
|
+
init_dist();
|
|
1224
1384
|
consecutiveTelemetryFailures = 0;
|
|
1225
1385
|
TELEMETRY_BLACKOUT_THRESHOLD = 3;
|
|
1226
1386
|
}
|
|
@@ -1285,7 +1445,7 @@ function wrapWithForwarding(instance) {
|
|
|
1285
1445
|
function createProxyAdapter(config) {
|
|
1286
1446
|
return wrapWithForwarding(new ProxyPapiAdapter(config));
|
|
1287
1447
|
}
|
|
1288
|
-
var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, NO_FORWARD, ProxyPapiAdapter;
|
|
1448
|
+
var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, HOSTED_SERVED, NO_FORWARD, ProxyPapiAdapter;
|
|
1289
1449
|
var init_proxy_adapter = __esm({
|
|
1290
1450
|
"src/proxy-adapter.ts"() {
|
|
1291
1451
|
"use strict";
|
|
@@ -1313,6 +1473,156 @@ var init_proxy_adapter = __esm({
|
|
|
1313
1473
|
"getRecentReviews",
|
|
1314
1474
|
"getActiveDecisions"
|
|
1315
1475
|
]);
|
|
1476
|
+
HOSTED_SERVED = /* @__PURE__ */ new Set([
|
|
1477
|
+
"actionRecommendation",
|
|
1478
|
+
"addAgendaTopic",
|
|
1479
|
+
"addContributorByEmail",
|
|
1480
|
+
"appendBuildReport",
|
|
1481
|
+
"appendCycleLearnings",
|
|
1482
|
+
"appendCycleMetrics",
|
|
1483
|
+
"appendDecisionEvent",
|
|
1484
|
+
"appendToolMetric",
|
|
1485
|
+
"archiveTasks",
|
|
1486
|
+
"claimTask",
|
|
1487
|
+
"clearPendingReviewResponse",
|
|
1488
|
+
"compressBuildReports",
|
|
1489
|
+
"compressCycleLog",
|
|
1490
|
+
"confirmPendingActiveDecisions",
|
|
1491
|
+
"correctLatestBuildReportEffort",
|
|
1492
|
+
"countDueOwnerActions",
|
|
1493
|
+
"countNudgedOwnerActions",
|
|
1494
|
+
"countOpenOwnerActions",
|
|
1495
|
+
"countOwnerActionsBlockingTasks",
|
|
1496
|
+
"countPlanRunsForCycle",
|
|
1497
|
+
"createConvention",
|
|
1498
|
+
"createCycle",
|
|
1499
|
+
"createHorizon",
|
|
1500
|
+
"createOwnerAction",
|
|
1501
|
+
"createStage",
|
|
1502
|
+
"createTask",
|
|
1503
|
+
"deleteActiveDecision",
|
|
1504
|
+
"deleteConvention",
|
|
1505
|
+
"deleteDoc",
|
|
1506
|
+
"dismissRecommendation",
|
|
1507
|
+
"findPendingDocActionsForTask",
|
|
1508
|
+
"getActiveDecisions",
|
|
1509
|
+
"getActiveStage",
|
|
1510
|
+
"getBuildReportCountForTask",
|
|
1511
|
+
"getBuildReportsSince",
|
|
1512
|
+
"getContextHashes",
|
|
1513
|
+
"getContextUtilisation",
|
|
1514
|
+
"getCostSnapshots",
|
|
1515
|
+
"getCostSummary",
|
|
1516
|
+
"getCurrentNorthStar",
|
|
1517
|
+
"getCycleHealth",
|
|
1518
|
+
"getCycleLearningPatterns",
|
|
1519
|
+
"getCycleLearnings",
|
|
1520
|
+
"getCycleLog",
|
|
1521
|
+
"getCycleLogSince",
|
|
1522
|
+
"getDecisionEvents",
|
|
1523
|
+
"getDecisionEventsSince",
|
|
1524
|
+
"getDecisionScorePatterns",
|
|
1525
|
+
"getDecisionScores",
|
|
1526
|
+
"getDecisionUsage",
|
|
1527
|
+
"getDoc",
|
|
1528
|
+
"getDocBody",
|
|
1529
|
+
"getDocBodyUsage",
|
|
1530
|
+
"getDogfoodLog",
|
|
1531
|
+
"getEstimationCalibration",
|
|
1532
|
+
"getFailedBuildAttemptsForTask",
|
|
1533
|
+
"getHarnessInventory",
|
|
1534
|
+
"getHarnessState",
|
|
1535
|
+
"getLastStrategyReviewCycle",
|
|
1536
|
+
"getLatestDecisionScores",
|
|
1537
|
+
"getModelOutcomeStats",
|
|
1538
|
+
"getModuleEstimationStats",
|
|
1539
|
+
"getNorthStarSetCycle",
|
|
1540
|
+
"getNorthStarStaleness",
|
|
1541
|
+
"getOwnerIdentity",
|
|
1542
|
+
"getPendingAgendaTopics",
|
|
1543
|
+
"getPendingRecommendations",
|
|
1544
|
+
"getPendingReviewResponse",
|
|
1545
|
+
"getPlanContextSummary",
|
|
1546
|
+
"getProjectInfo",
|
|
1547
|
+
"getProjectOwnerUserId",
|
|
1548
|
+
"getRecentBuildReports",
|
|
1549
|
+
"getRecentReviews",
|
|
1550
|
+
"getRecentTaskComments",
|
|
1551
|
+
"getRecommendationEffectiveness",
|
|
1552
|
+
"getStrategyReviews",
|
|
1553
|
+
"getTask",
|
|
1554
|
+
"getTasks",
|
|
1555
|
+
"getToolCallCount",
|
|
1556
|
+
"getUnactionedDogfoodEntries",
|
|
1557
|
+
"getUnnotifiedResolvedFeedback",
|
|
1558
|
+
"hasToolMilestone",
|
|
1559
|
+
"insertPlanRun",
|
|
1560
|
+
"insertToolRun",
|
|
1561
|
+
"linkOwnerActionToTask",
|
|
1562
|
+
"linkPhasesToStage",
|
|
1563
|
+
"listContributors",
|
|
1564
|
+
"listConventions",
|
|
1565
|
+
"listMyBugReports",
|
|
1566
|
+
"listOwnerActionsForBlockerScan",
|
|
1567
|
+
"logEntityReferences",
|
|
1568
|
+
"markAgendaTopicsAddressed",
|
|
1569
|
+
"markCycleLearningResolved",
|
|
1570
|
+
"markFeedbackNotified",
|
|
1571
|
+
"moveTask",
|
|
1572
|
+
"planWriteBack",
|
|
1573
|
+
"projectExists",
|
|
1574
|
+
"queryBoard",
|
|
1575
|
+
"readCycleMetrics",
|
|
1576
|
+
"readCycles",
|
|
1577
|
+
"readCycles",
|
|
1578
|
+
"readDiscoveryCanvas",
|
|
1579
|
+
"readHorizons",
|
|
1580
|
+
"readPhases",
|
|
1581
|
+
"readPlanningLog",
|
|
1582
|
+
"readProductBrief",
|
|
1583
|
+
"readRegistries",
|
|
1584
|
+
"readStages",
|
|
1585
|
+
"readToolMetrics",
|
|
1586
|
+
"recordProgressStep",
|
|
1587
|
+
"recordTransition",
|
|
1588
|
+
"registerDoc",
|
|
1589
|
+
"removeContributorByEmail",
|
|
1590
|
+
"reorderDocs",
|
|
1591
|
+
"replaceHarnessInventory",
|
|
1592
|
+
"resolveLearningsForDoneTasks",
|
|
1593
|
+
"savePendingReviewResponse",
|
|
1594
|
+
"searchDocs",
|
|
1595
|
+
"setCriterionMet",
|
|
1596
|
+
"setCycleHealth",
|
|
1597
|
+
"setHarnessState",
|
|
1598
|
+
"setProjectPapiDir",
|
|
1599
|
+
"storeDocBody",
|
|
1600
|
+
"submitBugReport",
|
|
1601
|
+
"unclaimTask",
|
|
1602
|
+
"updateActiveDecision",
|
|
1603
|
+
"updateCycleLearningActionRef",
|
|
1604
|
+
"updateDiscoveryCanvas",
|
|
1605
|
+
"updateDocAction",
|
|
1606
|
+
"updateDocStatus",
|
|
1607
|
+
"updateDogfoodEntryStatus",
|
|
1608
|
+
"updateHorizonStatus",
|
|
1609
|
+
"updatePhaseStatus",
|
|
1610
|
+
"updateProductBrief",
|
|
1611
|
+
"updateRegistries",
|
|
1612
|
+
"updateStageExitCriteria",
|
|
1613
|
+
"updateStageStatus",
|
|
1614
|
+
"updateTask",
|
|
1615
|
+
"updateTaskStatus",
|
|
1616
|
+
"upsertActiveDecision",
|
|
1617
|
+
"upsertNorthStar",
|
|
1618
|
+
"writeCycleLogEntry",
|
|
1619
|
+
"writeDecisionScore",
|
|
1620
|
+
"writeDogfoodEntries",
|
|
1621
|
+
"writePhases",
|
|
1622
|
+
"writeRecommendation",
|
|
1623
|
+
"writeReview",
|
|
1624
|
+
"writeStrategyReview"
|
|
1625
|
+
]);
|
|
1316
1626
|
NO_FORWARD = /* @__PURE__ */ new Set([
|
|
1317
1627
|
// (1) local-only
|
|
1318
1628
|
"close",
|
|
@@ -1326,6 +1636,13 @@ var init_proxy_adapter = __esm({
|
|
|
1326
1636
|
"commitReviewSubmit",
|
|
1327
1637
|
"commitRelease",
|
|
1328
1638
|
// (2) not-yet-wired hosted gaps — shrink as data-proxy handlers land (task-2390).
|
|
1639
|
+
// These Active Decision transaction helpers exist only on the direct-pg adapter.
|
|
1640
|
+
// The strategy service probes for them structurally and deliberately falls back to
|
|
1641
|
+
// the hosted-safe read/upsert paths when they are absent. Exposing them through the
|
|
1642
|
+
// generic trap sends an unwired method to data-proxy and turns that fallback into a
|
|
1643
|
+
// 403, blocking strategy_change and strategy_review on the canonical remote MCP.
|
|
1644
|
+
"allocateActiveDecision",
|
|
1645
|
+
"applyActiveDecisionUpdates",
|
|
1329
1646
|
// getToolCallCount + updatePhaseStatus are ABSENT: they now have edge handlers and
|
|
1330
1647
|
// are served through the forwarder (getToolCallCount = SUP-2026-026 handler #1).
|
|
1331
1648
|
"getContributorRole",
|
|
@@ -1399,6 +1716,30 @@ var init_proxy_adapter = __esm({
|
|
|
1399
1716
|
this.projectId = config.projectId ?? "";
|
|
1400
1717
|
this.onAuthRejected = config.onAuthRejected;
|
|
1401
1718
|
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Does the hosted path genuinely serve `name`? (task-3022, C362)
|
|
1721
|
+
*
|
|
1722
|
+
* This is the honest answer to the question `typeof adapter.name === 'function'`
|
|
1723
|
+
* USED to ask and could not answer: the get-trap below manufactures a function
|
|
1724
|
+
* for any name absent from NO_FORWARD, so a structural probe reads true for
|
|
1725
|
+
* pg-only methods and the caller then earns a 403 from the edge.
|
|
1726
|
+
*
|
|
1727
|
+
* Answered from NO_FORWARD itself, deliberately — the same set the forwarder
|
|
1728
|
+
* consults — so this can never disagree with what an actual call would do. A
|
|
1729
|
+
* name in NO_FORWARD is either local-only or not yet wired hosted; either way
|
|
1730
|
+
* the caller must take its fallback. The proxy-parity test guarantees every
|
|
1731
|
+
* method is an explicit wrapper, in NO_FORWARD, or in the edge allowlist, so
|
|
1732
|
+
* "not in NO_FORWARD" is a sound proxy for "served hosted".
|
|
1733
|
+
*
|
|
1734
|
+
* A REAL method, not a forwarded one: Reflect.get in the trap returns it before
|
|
1735
|
+
* the forwarding branch is reached, so callers get this implementation rather
|
|
1736
|
+
* than a forwarder that would POST "supportsMethod" to the edge.
|
|
1737
|
+
*/
|
|
1738
|
+
supportsMethod(name) {
|
|
1739
|
+
if (NO_FORWARD.has(name)) return false;
|
|
1740
|
+
if (name in _ProxyPapiAdapter.prototype) return true;
|
|
1741
|
+
return HOSTED_SERVED.has(name);
|
|
1742
|
+
}
|
|
1402
1743
|
/**
|
|
1403
1744
|
* task-1773: bearer-only auth probe. Hits the USER-scoped `project-list` route
|
|
1404
1745
|
* (no projectId needed), so it answers exactly one question: does the proxy
|
|
@@ -2175,6 +2516,7 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
2175
2516
|
import { pathToFileURL } from "url";
|
|
2176
2517
|
|
|
2177
2518
|
// src/adapter-factory.ts
|
|
2519
|
+
init_dist();
|
|
2178
2520
|
import path2 from "path";
|
|
2179
2521
|
import { execSync } from "child_process";
|
|
2180
2522
|
|
|
@@ -2259,8 +2601,18 @@ function detectUserId() {
|
|
|
2259
2601
|
}
|
|
2260
2602
|
return void 0;
|
|
2261
2603
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2604
|
+
function serverDeploymentProfile() {
|
|
2605
|
+
return resolveDeploymentProfile({
|
|
2606
|
+
selfHosted: process.env["PAPI_SELF_HOST"],
|
|
2607
|
+
appUrl: process.env["PAPI_DASHBOARD_URL"],
|
|
2608
|
+
mcpUrl: process.env["NEXT_PUBLIC_MCP_URL"],
|
|
2609
|
+
dataEndpoint: process.env["PAPI_DATA_ENDPOINT"],
|
|
2610
|
+
hostedSupabaseUrl: process.env["PAPI_HOSTED_SUPABASE_URL"],
|
|
2611
|
+
vercelEnabled: process.env["PAPI_ENABLE_VERCEL"],
|
|
2612
|
+
railwayEnabled: process.env["PAPI_ENABLE_RAILWAY"],
|
|
2613
|
+
managedSupabaseEnabled: process.env["PAPI_ENABLE_MANAGED_SUPABASE"]
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2264
2616
|
var PLACEHOLDER_PATTERNS = [
|
|
2265
2617
|
"<YOUR_DATABASE_URL>",
|
|
2266
2618
|
"your-database-url",
|
|
@@ -2496,7 +2848,8 @@ Fix the PAPI_PROJECT_ID in your .mcp.json (or unset it and let PAPI resolve the
|
|
|
2496
2848
|
}
|
|
2497
2849
|
case "proxy": {
|
|
2498
2850
|
const { ProxyPapiAdapter: ProxyPapiAdapter2, wrapWithForwarding: wrapWithForwarding2 } = await Promise.resolve().then(() => (init_proxy_adapter(), proxy_adapter_exports));
|
|
2499
|
-
const
|
|
2851
|
+
const deployment = serverDeploymentProfile();
|
|
2852
|
+
const dashboardUrl = deployment.appUrl;
|
|
2500
2853
|
const projectId = process.env["PAPI_PROJECT_ID"];
|
|
2501
2854
|
const dataApiKey = process.env["PAPI_DATA_API_KEY"];
|
|
2502
2855
|
if (!dataApiKey) {
|
|
@@ -2504,14 +2857,19 @@ Fix the PAPI_PROJECT_ID in your .mcp.json (or unset it and let PAPI resolve the
|
|
|
2504
2857
|
`PAPI needs an account to store your project data.
|
|
2505
2858
|
|
|
2506
2859
|
Get started in 3 steps:
|
|
2507
|
-
1. Sign up at ${dashboardUrl}/login
|
|
2860
|
+
1. Sign up at ${dashboardUrl ? `${dashboardUrl}/login` : "your configured PAPI dashboard"}
|
|
2508
2861
|
2. Complete the onboarding wizard \u2014 it generates your .mcp.json config
|
|
2509
2862
|
3. Download the config, place it in your project root, and restart Claude Code
|
|
2510
2863
|
|
|
2511
2864
|
Already have an account? Make sure PAPI_DATA_API_KEY is set in your .mcp.json env config.`
|
|
2512
2865
|
);
|
|
2513
2866
|
}
|
|
2514
|
-
const dataEndpoint =
|
|
2867
|
+
const dataEndpoint = deployment.dataEndpoint;
|
|
2868
|
+
if (!dataEndpoint) {
|
|
2869
|
+
throw new Error(
|
|
2870
|
+
"PAPI_DATA_ENDPOINT is required for the proxy adapter in a self-hosted deployment. Set it to the deployment-owned data-proxy URL; hosted production defaults are intentionally disabled."
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2515
2873
|
const adapter = new ProxyPapiAdapter2({
|
|
2516
2874
|
endpoint: dataEndpoint,
|
|
2517
2875
|
apiKey: dataApiKey,
|
|
@@ -2609,111 +2967,8 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
2609
2967
|
}
|
|
2610
2968
|
}
|
|
2611
2969
|
|
|
2612
|
-
// ../shared/dist/index.js
|
|
2613
|
-
var CAPABILITY_REGISTRY = [
|
|
2614
|
-
{ key: "prReviewer", label: "Auto code review", description: "Runs a code review of the branch diff before accepting.", step: "review" },
|
|
2615
|
-
{ key: "securityScan", label: "Security scan", description: "Flags a security pass on risk-tier changes at review time.", step: "review" },
|
|
2616
|
-
{ key: "changelog", label: "Changelog & cycle update", description: "Curates a cycle-update post when a release ships.", step: "release" },
|
|
2617
|
-
{ key: "verifyHealthCheck", label: "Release health check", description: "Reminds you to verify cycle state before release.", step: "release" },
|
|
2618
|
-
{ key: "gestaltPreBuild", label: "Gestalt pre-build check", description: "Reads the whole cycle before building the first task.", step: "build" },
|
|
2619
|
-
{ key: "batchBuildRollup", label: "Batch build rollup", description: "Emits a cross-task summary after a batch build.", step: "build" },
|
|
2620
|
-
{ key: "modelRecommendation", label: "Model recommendation", description: "Suggests a model tier for each task.", step: "build" },
|
|
2621
|
-
{ key: "discoveredIssues", label: "Discovered issues surfacing", description: "Surfaces issues logged during builds at release.", step: "release" },
|
|
2622
|
-
{ key: "publishDirective", label: "Publish & registry updates", description: "Announces the release and updates MCP registry listings.", step: "release" },
|
|
2623
|
-
// task-2482 (C319): the release quality gate. When on AND a gate command is
|
|
2624
|
-
// configured (PAPI_GATE), release makes the host LLM run that command and
|
|
2625
|
-
// BLOCKS the tag/merge on failure (fail-closed, AD-58 — PAPI never runs it).
|
|
2626
|
-
{ key: "releaseGate", label: "Release quality gate", description: "Runs your test/build command before release and blocks on failure.", step: "release" },
|
|
2627
|
-
// task-2491 (C320): post-release deploy hook — the deploy half of the ship→verify
|
|
2628
|
-
// loop. UNLIKE every other capability this one defaults OFF (defaultEnabled:false):
|
|
2629
|
-
// running a deploy command is destructive, so it is strictly opt-in. When ON AND
|
|
2630
|
-
// papi.deploy (PAPI_DEPLOY) is set, release emits a directive telling the HOST to
|
|
2631
|
-
// run the deploy command AFTER the merge/tag (AD-58 — PAPI never runs it) and
|
|
2632
|
-
// records a deploy_hook progress step so the reactive hub shows the deploy step.
|
|
2633
|
-
{ key: "deployHook", label: "Post-release deploy", description: "Runs your deploy command after a release merges.", step: "release", defaultEnabled: false },
|
|
2634
|
-
// task-2833 (C341): enforce the handoff's acceptanceCriteria[] at build-complete.
|
|
2635
|
-
// When ON, a completed:"yes" build whose handoff lists acceptance criteria must
|
|
2636
|
-
// pass acceptance_confirmed:true or build_execute returns the checklist and does
|
|
2637
|
-
// NOT mark the task Done (non-destructive — the report is not discarded). Defaults
|
|
2638
|
-
// OFF: it changes the completion contract, so it is strictly opt-in until a project
|
|
2639
|
-
// chooses to hold builds to their own acceptance criteria.
|
|
2640
|
-
{ key: "acceptanceGate", label: "Acceptance-criteria gate", description: "Requires confirming the handoff acceptance criteria before a build completes.", step: "build", defaultEnabled: false },
|
|
2641
|
-
// task-2325 (C344): configurable workflow guardrails — let a user opt out of the
|
|
2642
|
-
// git/branch/commit/PR ceremony and PAPI-meta framing so PAPI adapts to their
|
|
2643
|
-
// workflow instead of imposing one. Every toggle DEFAULTS ON (no defaultEnabled)
|
|
2644
|
-
// so current behaviour is byte-identical until the user flips it off. The no-git
|
|
2645
|
-
// fallback that these ride on top of is a separate task (task-2353).
|
|
2646
|
-
{ key: "autoBranch", label: "Auto branch", description: "Creates a feature branch per task/cycle at build start.", step: "build" },
|
|
2647
|
-
{ key: "autoCommit", label: "Auto commit", description: "Commits your work automatically when a build completes.", step: "build" },
|
|
2648
|
-
{ key: "autoPush", label: "Auto push & PR", description: "Pushes the branch and opens a pull request on build complete.", step: "build" },
|
|
2649
|
-
{ key: "papiMetaFraming", label: "PAPI self-referential framing", description: "Includes PAPI-meta build-discipline framing in handoff output.", step: "build" }
|
|
2650
|
-
];
|
|
2651
|
-
var CAPABILITY_KEYS = CAPABILITY_REGISTRY.map((c) => c.key);
|
|
2652
|
-
var WAB_WINDOW_DAYS = 7;
|
|
2653
|
-
var WAB_WEEK_MS = WAB_WINDOW_DAYS * 24 * 60 * 60 * 1e3;
|
|
2654
|
-
var EFFORT_SCALE = {
|
|
2655
|
-
XS: 1,
|
|
2656
|
-
S: 2,
|
|
2657
|
-
M: 3,
|
|
2658
|
-
L: 4,
|
|
2659
|
-
XL: 5
|
|
2660
|
-
};
|
|
2661
|
-
function effortOrdinal(effort) {
|
|
2662
|
-
if (typeof effort !== "string") return void 0;
|
|
2663
|
-
const normalized = effort.trim().toUpperCase();
|
|
2664
|
-
return EFFORT_SCALE[normalized];
|
|
2665
|
-
}
|
|
2666
|
-
function isUnparsedEffort(effort) {
|
|
2667
|
-
if (typeof effort !== "string" || effort.trim().length === 0) return false;
|
|
2668
|
-
return effortOrdinal(effort) === void 0;
|
|
2669
|
-
}
|
|
2670
|
-
function calculateCycleMetrics(reports, currentCycle, window = 5) {
|
|
2671
|
-
const recentReports = reports.filter(
|
|
2672
|
-
(r) => r.cycle > currentCycle - window && r.cycle <= currentCycle
|
|
2673
|
-
);
|
|
2674
|
-
const unparsedEffortCount = recentReports.filter(
|
|
2675
|
-
(r) => isUnparsedEffort(r.actualEffort) || isUnparsedEffort(r.estimatedEffort)
|
|
2676
|
-
).length;
|
|
2677
|
-
const perCycle = /* @__PURE__ */ new Map();
|
|
2678
|
-
for (const r of recentReports) {
|
|
2679
|
-
const group = perCycle.get(r.cycle) ?? [];
|
|
2680
|
-
group.push(r);
|
|
2681
|
-
perCycle.set(r.cycle, group);
|
|
2682
|
-
}
|
|
2683
|
-
const accuracy = [];
|
|
2684
|
-
const velocity = [];
|
|
2685
|
-
const sortedCycles = [...perCycle.keys()].sort((a, b) => a - b);
|
|
2686
|
-
for (const cycle of sortedCycles) {
|
|
2687
|
-
const reps = perCycle.get(cycle);
|
|
2688
|
-
const deltas = [];
|
|
2689
|
-
for (const r of reps) {
|
|
2690
|
-
const actual = effortOrdinal(r.actualEffort);
|
|
2691
|
-
const estimated = effortOrdinal(r.estimatedEffort);
|
|
2692
|
-
if (actual !== void 0 && estimated !== void 0) {
|
|
2693
|
-
deltas.push(actual - estimated);
|
|
2694
|
-
}
|
|
2695
|
-
}
|
|
2696
|
-
if (deltas.length > 0) {
|
|
2697
|
-
accuracy.push({
|
|
2698
|
-
cycle,
|
|
2699
|
-
reports: deltas.length,
|
|
2700
|
-
matchRate: Math.round(deltas.filter((d) => d === 0).length / deltas.length * 100),
|
|
2701
|
-
mae: Math.round(deltas.reduce((s, d) => s + Math.abs(d), 0) / deltas.length * 10) / 10,
|
|
2702
|
-
bias: Math.round(deltas.reduce((s, d) => s + d, 0) / deltas.length * 10) / 10
|
|
2703
|
-
});
|
|
2704
|
-
}
|
|
2705
|
-
velocity.push({
|
|
2706
|
-
cycle,
|
|
2707
|
-
completed: reps.filter((r) => r.completed === "Yes").length,
|
|
2708
|
-
partial: reps.filter((r) => r.completed === "Partial").length,
|
|
2709
|
-
failed: reps.filter((r) => r.completed === "No").length,
|
|
2710
|
-
effortPoints: reps.reduce((s, r) => s + (effortOrdinal(r.actualEffort) ?? 0), 0)
|
|
2711
|
-
});
|
|
2712
|
-
}
|
|
2713
|
-
return { accuracy, velocity, unparsedEffortCount };
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2716
2970
|
// src/lib/formatters.ts
|
|
2971
|
+
init_dist();
|
|
2717
2972
|
var PLAN_FULL_NOTES_BUDGET_BYTES = Number(process.env.PAPI_PLAN_NOTES_BUDGET) || 6e4;
|
|
2718
2973
|
function effortWeight(size) {
|
|
2719
2974
|
switch ((size || "").toUpperCase()) {
|