@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.
Files changed (94) hide show
  1. package/dist/routes/plugin-ui-static.d.ts.map +1 -1
  2. package/dist/routes/plugin-ui-static.js +8 -16
  3. package/dist/routes/plugin-ui-static.js.map +1 -1
  4. package/dist/services/environment-runtime.d.ts.map +1 -1
  5. package/dist/services/environment-runtime.js +30 -4
  6. package/dist/services/environment-runtime.js.map +1 -1
  7. package/dist/services/issues.d.ts +20 -0
  8. package/dist/services/issues.d.ts.map +1 -1
  9. package/dist/services/issues.js +153 -9
  10. package/dist/services/issues.js.map +1 -1
  11. package/dist/services/plugin-environment-driver.d.ts +2 -0
  12. package/dist/services/plugin-environment-driver.d.ts.map +1 -1
  13. package/dist/services/plugin-environment-driver.js +2 -0
  14. package/dist/services/plugin-environment-driver.js.map +1 -1
  15. package/package.json +16 -16
  16. package/ui-dist/assets/{_basePickBy-D9S5VT5H.js → _basePickBy-WhLEvV8W.js} +1 -1
  17. package/ui-dist/assets/{_baseUniq-CUx9E8q4.js → _baseUniq-CN3b-hNk.js} +1 -1
  18. package/ui-dist/assets/{arc-D1G6EiD8.js → arc-KqC1SLts.js} +1 -1
  19. package/ui-dist/assets/{architectureDiagram-VXUJARFQ-B6dEwowg.js → architectureDiagram-VXUJARFQ-BNPjF5nh.js} +1 -1
  20. package/ui-dist/assets/{blockDiagram-VD42YOAC-DLECI2U8.js → blockDiagram-VD42YOAC-BxjmsXqm.js} +1 -1
  21. package/ui-dist/assets/{browser-ponyfill-DsatNpG8.js → browser-ponyfill-BIRuJhm-.js} +1 -1
  22. package/ui-dist/assets/{c4Diagram-YG6GDRKO-DVjZIqCD.js → c4Diagram-YG6GDRKO-dEfIoCc5.js} +1 -1
  23. package/ui-dist/assets/channel-BsWAc6It.js +1 -0
  24. package/ui-dist/assets/{chunk-4BX2VUAB-Bi2BfCZB.js → chunk-4BX2VUAB-UtSWxzzP.js} +1 -1
  25. package/ui-dist/assets/{chunk-55IACEB6-DzsdKgdN.js → chunk-55IACEB6-DmlKHctG.js} +1 -1
  26. package/ui-dist/assets/{chunk-B4BG7PRW-DPQdF0Mm.js → chunk-B4BG7PRW-DMvWRqKK.js} +1 -1
  27. package/ui-dist/assets/{chunk-DI55MBZ5-E2mUXyxa.js → chunk-DI55MBZ5-EkeFVRto.js} +1 -1
  28. package/ui-dist/assets/{chunk-FMBD7UC4-rG-6SFuD.js → chunk-FMBD7UC4-DqNwNFJA.js} +1 -1
  29. package/ui-dist/assets/{chunk-QN33PNHL-RlKju2l3.js → chunk-QN33PNHL-BsfofdEf.js} +1 -1
  30. package/ui-dist/assets/{chunk-QZHKN3VN-Bcj6N4cS.js → chunk-QZHKN3VN-DuGgsgFe.js} +1 -1
  31. package/ui-dist/assets/{chunk-TZMSLE5B-BPf1EooF.js → chunk-TZMSLE5B-Dw49uCKc.js} +1 -1
  32. package/ui-dist/assets/classDiagram-2ON5EDUG-CBLKgzaX.js +1 -0
  33. package/ui-dist/assets/classDiagram-v2-WZHVMYZB-CBLKgzaX.js +1 -0
  34. package/ui-dist/assets/clone-yvIeiah6.js +1 -0
  35. package/ui-dist/assets/{cose-bilkent-S5V4N54A-BcBNmMIZ.js → cose-bilkent-S5V4N54A-DxAanjRd.js} +1 -1
  36. package/ui-dist/assets/{dagre-6UL2VRFP-XwjIVmZD.js → dagre-6UL2VRFP-BJryY2qB.js} +1 -1
  37. package/ui-dist/assets/{diagram-PSM6KHXK-DtPCuJs0.js → diagram-PSM6KHXK-CyA1T3Aq.js} +1 -1
  38. package/ui-dist/assets/{diagram-QEK2KX5R-Bgdl4KcE.js → diagram-QEK2KX5R-ZgiSVRpW.js} +1 -1
  39. package/ui-dist/assets/{diagram-S2PKOQOG-BXstqXyO.js → diagram-S2PKOQOG-C3C8W7t3.js} +1 -1
  40. package/ui-dist/assets/{erDiagram-Q2GNP2WA-HmVtYxtc.js → erDiagram-Q2GNP2WA-CQRuGux1.js} +1 -1
  41. package/ui-dist/assets/{flowDiagram-NV44I4VS-D9EALf1X.js → flowDiagram-NV44I4VS-2Gm9cL4y.js} +1 -1
  42. package/ui-dist/assets/{ganttDiagram-JELNMOA3-BNHNY9Z8.js → ganttDiagram-JELNMOA3-Dsvuq07f.js} +1 -1
  43. package/ui-dist/assets/{gitGraphDiagram-V2S2FVAM-CCg-O360.js → gitGraphDiagram-V2S2FVAM-DIqeAoUC.js} +1 -1
  44. package/ui-dist/assets/{graph-DbdVthjR.js → graph-CpPbYiAE.js} +1 -1
  45. package/ui-dist/assets/{index-cy0XBrca.js → index-90ums60G.js} +1 -1
  46. package/ui-dist/assets/{index-CUROdqJ9.js → index-B-OXspAj.js} +1 -1
  47. package/ui-dist/assets/{index-BOX51yfE.js → index-B9DzM6bN.js} +158 -158
  48. package/ui-dist/assets/{index-C45uXJzK.js → index-Bc8PcCs2.js} +1 -1
  49. package/ui-dist/assets/{index-QdzTiq1y.js → index-BgZNcDg_.js} +1 -1
  50. package/ui-dist/assets/{index-CiQp4WTO.js → index-BjFevjVd.js} +1 -1
  51. package/ui-dist/assets/{index-Glw1LCpc.js → index-Bmxq5evF.js} +1 -1
  52. package/ui-dist/assets/{index-HJ4G7YAF.js → index-Brimnq9S.js} +1 -1
  53. package/ui-dist/assets/{index-DBvul0X4.js → index-C14VhMpl.js} +1 -1
  54. package/ui-dist/assets/{index-brL_POlZ.js → index-CNZ3MC-s.js} +1 -1
  55. package/ui-dist/assets/{index-dCxbGPPF.js → index-CPAgV5MH.js} +1 -1
  56. package/ui-dist/assets/{index-DS19OSk3.js → index-CgzAyoOK.js} +1 -1
  57. package/ui-dist/assets/{index-D1u3F9K8.js → index-Ch29rBO2.js} +1 -1
  58. package/ui-dist/assets/{index-CtsTYjB3.js → index-CqbT-E3E.js} +1 -1
  59. package/ui-dist/assets/{index-CPgJS3gd.js → index-CwriUWwk.js} +1 -1
  60. package/ui-dist/assets/{index-BT5_v_DC.js → index-D2Pn4kgt.js} +1 -1
  61. package/ui-dist/assets/{index-BMMbeaRL.js → index-D8yiqVsj.js} +1 -1
  62. package/ui-dist/assets/{index-CIfdaMBq.js → index-DRQwDLzh.js} +1 -1
  63. package/ui-dist/assets/{index-Cih3OYXQ.js → index-DU1KuXiN.js} +1 -1
  64. package/ui-dist/assets/{index-LBebTBEm.js → index-Dhds1Vug.js} +1 -1
  65. package/ui-dist/assets/{index-B7F_Kj9X.js → index-DkUb_-Cu.js} +1 -1
  66. package/ui-dist/assets/{index-BJ6fiPmR.js → index-FpYrFWtW.js} +1 -1
  67. package/ui-dist/assets/index-rZG_Ge2N.css +1 -0
  68. package/ui-dist/assets/{index-rHsLi0O7.js → index-tdSzXUkL.js} +1 -1
  69. package/ui-dist/assets/{infoDiagram-HS3SLOUP-OCmjwWtI.js → infoDiagram-HS3SLOUP-CePhP7Rm.js} +1 -1
  70. package/ui-dist/assets/{journeyDiagram-XKPGCS4Q-DC4V58Dm.js → journeyDiagram-XKPGCS4Q-Db4zBH9I.js} +1 -1
  71. package/ui-dist/assets/{kanban-definition-3W4ZIXB7-BMjB-TsZ.js → kanban-definition-3W4ZIXB7-CRpxKVeo.js} +1 -1
  72. package/ui-dist/assets/{layout-mmOX6t1K.js → layout-Dayu6Q_G.js} +1 -1
  73. package/ui-dist/assets/{linear-BXHccZ98.js → linear-GEZW_Ad4.js} +1 -1
  74. package/ui-dist/assets/{mermaid.core-CAzEyqWL.js → mermaid.core-DYUBZZQy.js} +4 -4
  75. package/ui-dist/assets/{mindmap-definition-VGOIOE7T-C0gKkuUP.js → mindmap-definition-VGOIOE7T-Cy_3to40.js} +1 -1
  76. package/ui-dist/assets/{pieDiagram-ADFJNKIX-Bw04KqFX.js → pieDiagram-ADFJNKIX-OL0yaQog.js} +1 -1
  77. package/ui-dist/assets/{quadrantDiagram-AYHSOK5B-C73cxROu.js → quadrantDiagram-AYHSOK5B-CFd61oDJ.js} +1 -1
  78. package/ui-dist/assets/{requirementDiagram-UZGBJVZJ-YxuRMDcZ.js → requirementDiagram-UZGBJVZJ-BrCtg0TQ.js} +1 -1
  79. package/ui-dist/assets/{sankeyDiagram-TZEHDZUN-BsO-nx3o.js → sankeyDiagram-TZEHDZUN-CZCRRLmp.js} +1 -1
  80. package/ui-dist/assets/{sequenceDiagram-WL72ISMW-BoH9Tcfu.js → sequenceDiagram-WL72ISMW-Cc7NL8-F.js} +1 -1
  81. package/ui-dist/assets/{stateDiagram-FKZM4ZOC-CW6NYkyp.js → stateDiagram-FKZM4ZOC-DF0-vIL7.js} +1 -1
  82. package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-BnL7t6v5.js +1 -0
  83. package/ui-dist/assets/{timeline-definition-IT6M3QCI-BBOQviLF.js → timeline-definition-IT6M3QCI-CkuH47H1.js} +1 -1
  84. package/ui-dist/assets/{treemap-GDKQZRPO-CRWmD4cw.js → treemap-GDKQZRPO-CHG8vhYQ.js} +1 -1
  85. package/ui-dist/assets/{xychartDiagram-PRI3JC2R-CFOSwXiv.js → xychartDiagram-PRI3JC2R-DC9ulqdM.js} +1 -1
  86. package/ui-dist/index.html +2 -2
  87. package/ui-dist/locales/en/common.json +8 -0
  88. package/ui-dist/locales/zh-CN/common.json +8 -0
  89. package/ui-dist/assets/channel-CrFfzUPj.js +0 -1
  90. package/ui-dist/assets/classDiagram-2ON5EDUG-CC7eeb3L.js +0 -1
  91. package/ui-dist/assets/classDiagram-v2-WZHVMYZB-CC7eeb3L.js +0 -1
  92. package/ui-dist/assets/clone-C_hn37pO.js +0 -1
  93. package/ui-dist/assets/index-DhYPnoTf.css +0 -1
  94. package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-upuqx2TO.js +0 -1
@@ -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
- return comments.map((comment) => redactIssueComment(comment, censorUsernameInLogs));
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) => instanceSettings.getGeneral().then(({ censorUsernameInLogs }) => db
2889
- .select()
2890
- .from(issueComments)
2891
- .where(eq(issueComments.id, commentId))
2892
- .then((rows) => {
2893
- const comment = rows[0] ?? null;
2894
- return comment ? redactIssueComment(comment, censorUsernameInLogs) : null;
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,