@penclipai/server 2026.511.1 → 2026.512.0
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/routes/plugin-ui-static.d.ts.map +1 -1
- package/dist/routes/plugin-ui-static.js +8 -16
- package/dist/routes/plugin-ui-static.js.map +1 -1
- package/dist/services/environment-runtime.d.ts.map +1 -1
- package/dist/services/environment-runtime.js +30 -4
- package/dist/services/environment-runtime.js.map +1 -1
- package/dist/services/issues.d.ts +20 -0
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.js +153 -9
- package/dist/services/issues.js.map +1 -1
- package/dist/services/plugin-environment-driver.d.ts +2 -0
- package/dist/services/plugin-environment-driver.d.ts.map +1 -1
- package/dist/services/plugin-environment-driver.js +2 -0
- package/dist/services/plugin-environment-driver.js.map +1 -1
- package/package.json +16 -16
- package/ui-dist/assets/{_basePickBy-D9S5VT5H.js → _basePickBy-WhLEvV8W.js} +1 -1
- package/ui-dist/assets/{_baseUniq-CUx9E8q4.js → _baseUniq-CN3b-hNk.js} +1 -1
- package/ui-dist/assets/{arc-D1G6EiD8.js → arc-KqC1SLts.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-VXUJARFQ-B6dEwowg.js → architectureDiagram-VXUJARFQ-BNPjF5nh.js} +1 -1
- package/ui-dist/assets/{blockDiagram-VD42YOAC-DLECI2U8.js → blockDiagram-VD42YOAC-BxjmsXqm.js} +1 -1
- package/ui-dist/assets/{browser-ponyfill-DsatNpG8.js → browser-ponyfill-BIRuJhm-.js} +1 -1
- package/ui-dist/assets/{c4Diagram-YG6GDRKO-DVjZIqCD.js → c4Diagram-YG6GDRKO-dEfIoCc5.js} +1 -1
- package/ui-dist/assets/channel-BsWAc6It.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-Bi2BfCZB.js → chunk-4BX2VUAB-UtSWxzzP.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-DzsdKgdN.js → chunk-55IACEB6-DmlKHctG.js} +1 -1
- package/ui-dist/assets/{chunk-B4BG7PRW-DPQdF0Mm.js → chunk-B4BG7PRW-DMvWRqKK.js} +1 -1
- package/ui-dist/assets/{chunk-DI55MBZ5-E2mUXyxa.js → chunk-DI55MBZ5-EkeFVRto.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-rG-6SFuD.js → chunk-FMBD7UC4-DqNwNFJA.js} +1 -1
- package/ui-dist/assets/{chunk-QN33PNHL-RlKju2l3.js → chunk-QN33PNHL-BsfofdEf.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-Bcj6N4cS.js → chunk-QZHKN3VN-DuGgsgFe.js} +1 -1
- package/ui-dist/assets/{chunk-TZMSLE5B-BPf1EooF.js → chunk-TZMSLE5B-Dw49uCKc.js} +1 -1
- package/ui-dist/assets/classDiagram-2ON5EDUG-CBLKgzaX.js +1 -0
- package/ui-dist/assets/classDiagram-v2-WZHVMYZB-CBLKgzaX.js +1 -0
- package/ui-dist/assets/clone-yvIeiah6.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-BcBNmMIZ.js → cose-bilkent-S5V4N54A-DxAanjRd.js} +1 -1
- package/ui-dist/assets/{dagre-6UL2VRFP-XwjIVmZD.js → dagre-6UL2VRFP-BJryY2qB.js} +1 -1
- package/ui-dist/assets/{diagram-PSM6KHXK-DtPCuJs0.js → diagram-PSM6KHXK-CyA1T3Aq.js} +1 -1
- package/ui-dist/assets/{diagram-QEK2KX5R-Bgdl4KcE.js → diagram-QEK2KX5R-ZgiSVRpW.js} +1 -1
- package/ui-dist/assets/{diagram-S2PKOQOG-BXstqXyO.js → diagram-S2PKOQOG-C3C8W7t3.js} +1 -1
- package/ui-dist/assets/{erDiagram-Q2GNP2WA-HmVtYxtc.js → erDiagram-Q2GNP2WA-CQRuGux1.js} +1 -1
- package/ui-dist/assets/{flowDiagram-NV44I4VS-D9EALf1X.js → flowDiagram-NV44I4VS-2Gm9cL4y.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-JELNMOA3-BNHNY9Z8.js → ganttDiagram-JELNMOA3-Dsvuq07f.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-V2S2FVAM-CCg-O360.js → gitGraphDiagram-V2S2FVAM-DIqeAoUC.js} +1 -1
- package/ui-dist/assets/{graph-DbdVthjR.js → graph-CpPbYiAE.js} +1 -1
- package/ui-dist/assets/{index-cy0XBrca.js → index-90ums60G.js} +1 -1
- package/ui-dist/assets/{index-CUROdqJ9.js → index-B-OXspAj.js} +1 -1
- package/ui-dist/assets/{index-BOX51yfE.js → index-B9DzM6bN.js} +158 -158
- package/ui-dist/assets/{index-C45uXJzK.js → index-Bc8PcCs2.js} +1 -1
- package/ui-dist/assets/{index-QdzTiq1y.js → index-BgZNcDg_.js} +1 -1
- package/ui-dist/assets/{index-CiQp4WTO.js → index-BjFevjVd.js} +1 -1
- package/ui-dist/assets/{index-Glw1LCpc.js → index-Bmxq5evF.js} +1 -1
- package/ui-dist/assets/{index-HJ4G7YAF.js → index-Brimnq9S.js} +1 -1
- package/ui-dist/assets/{index-DBvul0X4.js → index-C14VhMpl.js} +1 -1
- package/ui-dist/assets/{index-brL_POlZ.js → index-CNZ3MC-s.js} +1 -1
- package/ui-dist/assets/{index-dCxbGPPF.js → index-CPAgV5MH.js} +1 -1
- package/ui-dist/assets/{index-DS19OSk3.js → index-CgzAyoOK.js} +1 -1
- package/ui-dist/assets/{index-D1u3F9K8.js → index-Ch29rBO2.js} +1 -1
- package/ui-dist/assets/{index-CtsTYjB3.js → index-CqbT-E3E.js} +1 -1
- package/ui-dist/assets/{index-CPgJS3gd.js → index-CwriUWwk.js} +1 -1
- package/ui-dist/assets/{index-BT5_v_DC.js → index-D2Pn4kgt.js} +1 -1
- package/ui-dist/assets/{index-BMMbeaRL.js → index-D8yiqVsj.js} +1 -1
- package/ui-dist/assets/{index-CIfdaMBq.js → index-DRQwDLzh.js} +1 -1
- package/ui-dist/assets/{index-Cih3OYXQ.js → index-DU1KuXiN.js} +1 -1
- package/ui-dist/assets/{index-LBebTBEm.js → index-Dhds1Vug.js} +1 -1
- package/ui-dist/assets/{index-B7F_Kj9X.js → index-DkUb_-Cu.js} +1 -1
- package/ui-dist/assets/{index-BJ6fiPmR.js → index-FpYrFWtW.js} +1 -1
- package/ui-dist/assets/index-rZG_Ge2N.css +1 -0
- package/ui-dist/assets/{index-rHsLi0O7.js → index-tdSzXUkL.js} +1 -1
- package/ui-dist/assets/{infoDiagram-HS3SLOUP-OCmjwWtI.js → infoDiagram-HS3SLOUP-CePhP7Rm.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-XKPGCS4Q-DC4V58Dm.js → journeyDiagram-XKPGCS4Q-Db4zBH9I.js} +1 -1
- package/ui-dist/assets/{kanban-definition-3W4ZIXB7-BMjB-TsZ.js → kanban-definition-3W4ZIXB7-CRpxKVeo.js} +1 -1
- package/ui-dist/assets/{layout-mmOX6t1K.js → layout-Dayu6Q_G.js} +1 -1
- package/ui-dist/assets/{linear-BXHccZ98.js → linear-GEZW_Ad4.js} +1 -1
- package/ui-dist/assets/{mermaid.core-CAzEyqWL.js → mermaid.core-DYUBZZQy.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-VGOIOE7T-C0gKkuUP.js → mindmap-definition-VGOIOE7T-Cy_3to40.js} +1 -1
- package/ui-dist/assets/{pieDiagram-ADFJNKIX-Bw04KqFX.js → pieDiagram-ADFJNKIX-OL0yaQog.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-AYHSOK5B-C73cxROu.js → quadrantDiagram-AYHSOK5B-CFd61oDJ.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-UZGBJVZJ-YxuRMDcZ.js → requirementDiagram-UZGBJVZJ-BrCtg0TQ.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-TZEHDZUN-BsO-nx3o.js → sankeyDiagram-TZEHDZUN-CZCRRLmp.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-WL72ISMW-BoH9Tcfu.js → sequenceDiagram-WL72ISMW-Cc7NL8-F.js} +1 -1
- package/ui-dist/assets/{stateDiagram-FKZM4ZOC-CW6NYkyp.js → stateDiagram-FKZM4ZOC-DF0-vIL7.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-BnL7t6v5.js +1 -0
- package/ui-dist/assets/{timeline-definition-IT6M3QCI-BBOQviLF.js → timeline-definition-IT6M3QCI-CkuH47H1.js} +1 -1
- package/ui-dist/assets/{treemap-GDKQZRPO-CRWmD4cw.js → treemap-GDKQZRPO-CHG8vhYQ.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-PRI3JC2R-CFOSwXiv.js → xychartDiagram-PRI3JC2R-DC9ulqdM.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/locales/en/common.json +8 -0
- package/ui-dist/locales/zh-CN/common.json +8 -0
- package/ui-dist/assets/channel-CrFfzUPj.js +0 -1
- package/ui-dist/assets/classDiagram-2ON5EDUG-CC7eeb3L.js +0 -1
- package/ui-dist/assets/classDiagram-v2-WZHVMYZB-CC7eeb3L.js +0 -1
- package/ui-dist/assets/clone-C_hn37pO.js +0 -1
- package/ui-dist/assets/index-DhYPnoTf.css +0 -1
- package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-upuqx2TO.js +0 -1
package/dist/services/issues.js
CHANGED
|
@@ -10,6 +10,7 @@ import { buildInitialIssueMonitorFields, normalizeIssueExecutionPolicy } from ".
|
|
|
10
10
|
import { instanceSettingsService } from "./instance-settings.js";
|
|
11
11
|
import { redactCurrentUserText } from "../log-redaction.js";
|
|
12
12
|
import { resolveIssueGoalId, resolveNextIssueGoalId } from "./issue-goal-fallback.js";
|
|
13
|
+
import { getRunLogStore } from "./run-log-store.js";
|
|
13
14
|
import { getDefaultCompanyGoal } from "./goals.js";
|
|
14
15
|
import { isVerifiedIssueTreeControlInteractionWake, issueTreeControlService, } from "./issue-tree-control.js";
|
|
15
16
|
import { parseIssueGraphLivenessIncidentKey } from "./recovery/origins.js";
|
|
@@ -21,6 +22,10 @@ const ISSUE_LIST_RELATED_QUERY_CHUNK_SIZE = 500;
|
|
|
21
22
|
export const MAX_CHILD_ISSUES_CREATED_BY_HELPER = 25;
|
|
22
23
|
const MAX_CHILD_COMPLETION_SUMMARIES = 20;
|
|
23
24
|
const CHILD_COMPLETION_SUMMARY_BODY_MAX_CHARS = 500;
|
|
25
|
+
const ISSUE_COMMENT_RUN_LOG_DERIVATION_MAX_LOG_BYTES = 2_000_000;
|
|
26
|
+
const ISSUE_COMMENT_RUN_LOG_DERIVATION_CHUNK_BYTES = 256_000;
|
|
27
|
+
const ISSUE_COMMENT_RUN_LOG_DERIVATION_END_SLACK_MS = 60_000;
|
|
28
|
+
const ISSUE_COMMENT_RUN_LOG_DERIVATION_MAX_PARALLEL_READS = 8;
|
|
24
29
|
function assertTransition(from, to) {
|
|
25
30
|
if (from === to)
|
|
26
31
|
return;
|
|
@@ -56,6 +61,52 @@ function buildReusedExecutionWorkspaceConfigPatchFromIssueSettings(settings) {
|
|
|
56
61
|
workspaceRuntime: settings?.workspaceRuntime ?? null,
|
|
57
62
|
};
|
|
58
63
|
}
|
|
64
|
+
function toTimestampMs(value) {
|
|
65
|
+
if (!value)
|
|
66
|
+
return null;
|
|
67
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
68
|
+
const timestamp = date.getTime();
|
|
69
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
70
|
+
}
|
|
71
|
+
export function deriveIssueCommentRunLogAttribution(comments, runs) {
|
|
72
|
+
const derivedByCommentId = new Map();
|
|
73
|
+
for (const comment of comments) {
|
|
74
|
+
if (comment.authorAgentId || !comment.authorUserId || comment.createdByRunId)
|
|
75
|
+
continue;
|
|
76
|
+
const commentCreatedAtMs = toTimestampMs(comment.createdAt);
|
|
77
|
+
if (commentCreatedAtMs === null)
|
|
78
|
+
continue;
|
|
79
|
+
let bestMatch = null;
|
|
80
|
+
for (const run of runs) {
|
|
81
|
+
const runStartMs = toTimestampMs(run.startedAt ?? run.createdAt);
|
|
82
|
+
const runEndMs = toTimestampMs(run.finishedAt ?? run.createdAt);
|
|
83
|
+
if (runStartMs === null || runEndMs === null)
|
|
84
|
+
continue;
|
|
85
|
+
if (commentCreatedAtMs < runStartMs
|
|
86
|
+
|| commentCreatedAtMs > runEndMs + ISSUE_COMMENT_RUN_LOG_DERIVATION_END_SLACK_MS) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (!run.logContent.includes(`comment id: ${comment.id}`))
|
|
90
|
+
continue;
|
|
91
|
+
const distanceMs = Math.abs(runEndMs - commentCreatedAtMs);
|
|
92
|
+
if (!bestMatch || distanceMs < bestMatch.distanceMs) {
|
|
93
|
+
bestMatch = {
|
|
94
|
+
runId: run.runId,
|
|
95
|
+
agentId: run.agentId,
|
|
96
|
+
distanceMs,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!bestMatch)
|
|
101
|
+
continue;
|
|
102
|
+
derivedByCommentId.set(comment.id, {
|
|
103
|
+
derivedAuthorAgentId: bestMatch.agentId,
|
|
104
|
+
derivedCreatedByRunId: bestMatch.runId,
|
|
105
|
+
derivedAuthorSource: "run_log_comment_post",
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return derivedByCommentId;
|
|
109
|
+
}
|
|
59
110
|
function sameRunLock(checkoutRunId, actorRunId) {
|
|
60
111
|
if (actorRunId)
|
|
61
112
|
return checkoutRunId === actorRunId;
|
|
@@ -1272,6 +1323,94 @@ export function issueService(db) {
|
|
|
1272
1323
|
metadata: issueCommentMetadataSchema.nullable().catch(null).parse(comment.metadata ?? null),
|
|
1273
1324
|
};
|
|
1274
1325
|
}
|
|
1326
|
+
async function readRunLogText(run) {
|
|
1327
|
+
if (run.logStore !== "local_file" || !run.logRef)
|
|
1328
|
+
return "";
|
|
1329
|
+
const logBytes = Number(run.logBytes ?? 0);
|
|
1330
|
+
if (!Number.isFinite(logBytes) || logBytes <= 0)
|
|
1331
|
+
return "";
|
|
1332
|
+
const store = getRunLogStore();
|
|
1333
|
+
let offset = 0;
|
|
1334
|
+
let content = "";
|
|
1335
|
+
let nextOffset = 0;
|
|
1336
|
+
while (nextOffset !== undefined) {
|
|
1337
|
+
const remainingBytes = ISSUE_COMMENT_RUN_LOG_DERIVATION_MAX_LOG_BYTES - Buffer.byteLength(content, "utf8");
|
|
1338
|
+
if (remainingBytes <= 0)
|
|
1339
|
+
break;
|
|
1340
|
+
const chunk = await store.read({ store: "local_file", logRef: run.logRef }, {
|
|
1341
|
+
offset,
|
|
1342
|
+
limitBytes: Math.min(ISSUE_COMMENT_RUN_LOG_DERIVATION_CHUNK_BYTES, remainingBytes),
|
|
1343
|
+
});
|
|
1344
|
+
content += chunk.content;
|
|
1345
|
+
nextOffset = chunk.nextOffset;
|
|
1346
|
+
offset = chunk.nextOffset ?? 0;
|
|
1347
|
+
}
|
|
1348
|
+
return content;
|
|
1349
|
+
}
|
|
1350
|
+
async function enrichCommentsWithDerivedAgentAttribution(comments) {
|
|
1351
|
+
const candidates = comments.filter((comment) => !comment.authorAgentId
|
|
1352
|
+
&& !!comment.authorUserId
|
|
1353
|
+
&& !comment.createdByRunId);
|
|
1354
|
+
if (candidates.length === 0)
|
|
1355
|
+
return comments;
|
|
1356
|
+
const companyId = comments[0]?.companyId ?? null;
|
|
1357
|
+
const issueId = comments[0]?.issueId ?? null;
|
|
1358
|
+
if (!companyId || !issueId)
|
|
1359
|
+
return comments;
|
|
1360
|
+
const minCommentCreatedAtMs = candidates.reduce((min, comment) => {
|
|
1361
|
+
const timestamp = toTimestampMs(comment.createdAt);
|
|
1362
|
+
if (timestamp === null)
|
|
1363
|
+
return min;
|
|
1364
|
+
return min === null ? timestamp : Math.min(min, timestamp);
|
|
1365
|
+
}, null);
|
|
1366
|
+
const maxCommentCreatedAtMs = candidates.reduce((max, comment) => {
|
|
1367
|
+
const timestamp = toTimestampMs(comment.createdAt);
|
|
1368
|
+
if (timestamp === null)
|
|
1369
|
+
return max;
|
|
1370
|
+
return max === null ? timestamp : Math.max(max, timestamp);
|
|
1371
|
+
}, null);
|
|
1372
|
+
if (minCommentCreatedAtMs === null || maxCommentCreatedAtMs === null)
|
|
1373
|
+
return comments;
|
|
1374
|
+
const runs = await db
|
|
1375
|
+
.select({
|
|
1376
|
+
runId: heartbeatRuns.id,
|
|
1377
|
+
agentId: heartbeatRuns.agentId,
|
|
1378
|
+
createdAt: heartbeatRuns.createdAt,
|
|
1379
|
+
startedAt: heartbeatRuns.startedAt,
|
|
1380
|
+
finishedAt: heartbeatRuns.finishedAt,
|
|
1381
|
+
logStore: heartbeatRuns.logStore,
|
|
1382
|
+
logRef: heartbeatRuns.logRef,
|
|
1383
|
+
logBytes: heartbeatRuns.logBytes,
|
|
1384
|
+
})
|
|
1385
|
+
.from(heartbeatRuns)
|
|
1386
|
+
.where(and(eq(heartbeatRuns.companyId, companyId), or(sql `${heartbeatRuns.contextSnapshot} ->> 'issueId' = ${issueId}`, sql `exists (
|
|
1387
|
+
select 1
|
|
1388
|
+
from ${activityLog}
|
|
1389
|
+
where ${activityLog.companyId} = ${companyId}
|
|
1390
|
+
and ${activityLog.entityType} = 'issue'
|
|
1391
|
+
and ${activityLog.entityId} = ${issueId}
|
|
1392
|
+
and ${activityLog.runId} = ${heartbeatRuns.id}
|
|
1393
|
+
)`), sql `coalesce(${heartbeatRuns.finishedAt}, ${heartbeatRuns.createdAt}) >= ${new Date(minCommentCreatedAtMs)}`, sql `coalesce(${heartbeatRuns.startedAt}, ${heartbeatRuns.createdAt}) <= ${new Date(maxCommentCreatedAtMs + ISSUE_COMMENT_RUN_LOG_DERIVATION_END_SLACK_MS)}`))
|
|
1394
|
+
.orderBy(desc(heartbeatRuns.createdAt));
|
|
1395
|
+
if (runs.length === 0)
|
|
1396
|
+
return comments;
|
|
1397
|
+
const runsWithLogs = [];
|
|
1398
|
+
for (let index = 0; index < runs.length; index += ISSUE_COMMENT_RUN_LOG_DERIVATION_MAX_PARALLEL_READS) {
|
|
1399
|
+
const batch = runs.slice(index, index + ISSUE_COMMENT_RUN_LOG_DERIVATION_MAX_PARALLEL_READS);
|
|
1400
|
+
const batchWithLogs = await Promise.all(batch.map(async (run) => ({
|
|
1401
|
+
...run,
|
|
1402
|
+
logContent: await readRunLogText(run),
|
|
1403
|
+
})));
|
|
1404
|
+
runsWithLogs.push(...batchWithLogs);
|
|
1405
|
+
}
|
|
1406
|
+
const derivedByCommentId = deriveIssueCommentRunLogAttribution(candidates, runsWithLogs);
|
|
1407
|
+
if (derivedByCommentId.size === 0)
|
|
1408
|
+
return comments;
|
|
1409
|
+
return comments.map((comment) => {
|
|
1410
|
+
const derived = derivedByCommentId.get(comment.id);
|
|
1411
|
+
return derived ? { ...comment, ...derived } : comment;
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1275
1414
|
async function assertAssignableAgent(companyId, agentId) {
|
|
1276
1415
|
const assignee = await db
|
|
1277
1416
|
.select({
|
|
@@ -2857,7 +2996,8 @@ export function issueService(db) {
|
|
|
2857
2996
|
.orderBy(order === "asc" ? asc(issueComments.createdAt) : desc(issueComments.createdAt), order === "asc" ? asc(issueComments.id) : desc(issueComments.id));
|
|
2858
2997
|
const comments = limit ? await query.limit(limit) : await query;
|
|
2859
2998
|
const { censorUsernameInLogs } = await instanceSettings.getGeneral();
|
|
2860
|
-
|
|
2999
|
+
const enrichedComments = await enrichCommentsWithDerivedAgentAttribution(comments);
|
|
3000
|
+
return enrichedComments.map((comment) => redactIssueComment(comment, censorUsernameInLogs));
|
|
2861
3001
|
},
|
|
2862
3002
|
getCommentCursor: async (issueId) => {
|
|
2863
3003
|
const [latest, countRow] = await Promise.all([
|
|
@@ -2885,14 +3025,18 @@ export function issueService(db) {
|
|
|
2885
3025
|
latestCommentAt: latest?.latestCommentAt ?? null,
|
|
2886
3026
|
};
|
|
2887
3027
|
},
|
|
2888
|
-
getComment: (commentId) =>
|
|
2889
|
-
.
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
3028
|
+
getComment: async (commentId) => {
|
|
3029
|
+
const { censorUsernameInLogs } = await instanceSettings.getGeneral();
|
|
3030
|
+
const comment = await db
|
|
3031
|
+
.select()
|
|
3032
|
+
.from(issueComments)
|
|
3033
|
+
.where(eq(issueComments.id, commentId))
|
|
3034
|
+
.then((rows) => rows[0] ?? null);
|
|
3035
|
+
if (!comment)
|
|
3036
|
+
return null;
|
|
3037
|
+
const [enrichedComment] = await enrichCommentsWithDerivedAgentAttribution([comment]);
|
|
3038
|
+
return redactIssueComment(enrichedComment ?? comment, censorUsernameInLogs);
|
|
3039
|
+
},
|
|
2896
3040
|
removeComment: async (commentId) => {
|
|
2897
3041
|
const currentUserRedactionOptions = {
|
|
2898
3042
|
enabled: (await instanceSettings.getGeneral()).censorUsernameInLogs,
|