mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-26 18:31:34 +01:00
Optimize Hermes wake context prompt
This commit is contained in:
1 parent
fb11ed07fb
commit
8032c1364b
3 files changed
+243
-97
No files matched your search
@@ -32,6 +32,10 @@ import {
|
||||
buildPaperclipEnv,
|
||||
renderTemplate,
|
||||
ensureAbsoluteDirectory,
|
||||
DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE,
|
||||
joinPromptSections,
|
||||
renderPaperclipWakePrompt,
|
||||
stringifyPaperclipWakePayload,
|
||||
} from "@paperclipai/adapter-utils/server-utils";
|
||||
|
||||
import {
|
||||
@@ -74,76 +78,65 @@ export function resolveHermesCommand(config: Record<string, unknown>): string {
|
||||
// Wake-up prompt builder
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const DEFAULT_PROMPT_TEMPLATE = `You are "{{agentName}}", an AI agent employee in a Paperclip-managed company.
|
||||
const HERMES_DEFAULT_PROMPT_TEMPLATE = [
|
||||
'You are "{{agent.name}}", an AI agent employee in a Paperclip-managed company.',
|
||||
"",
|
||||
"Paperclip runtime identity:",
|
||||
"- Agent ID: {{agent.id}}",
|
||||
"- Company ID: {{agent.companyId}}",
|
||||
"- Run ID: {{run.id}}",
|
||||
"- API base: {{paperclipApiUrl}}",
|
||||
"",
|
||||
"Paperclip API guidance:",
|
||||
"- Use `curl` from the terminal for Paperclip API calls; browser/web extraction tools may not reach localhost.",
|
||||
"- Use `$PAPERCLIP_API_URL`, `$PAPERCLIP_API_KEY`, and `$PAPERCLIP_RUN_ID`; do not hard-code local ports or copy secrets into comments.",
|
||||
"- Include `-H \"Authorization: Bearer $PAPERCLIP_API_KEY\"` on API requests.",
|
||||
"- Include `-H \"X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID\"` on mutating issue requests.",
|
||||
"- For multiline comments or status updates, preserve newlines with `jq --arg` or a heredoc-fed helper rather than hand-escaping JSON.",
|
||||
"",
|
||||
"Safe multiline update pattern:",
|
||||
"```bash",
|
||||
"api=\"${PAPERCLIP_API_URL%/}\"",
|
||||
"case \"$api\" in */api) ;; *) api=\"$api/api\" ;; esac",
|
||||
"",
|
||||
"body=$(cat <<'MD'",
|
||||
"Summary line",
|
||||
"",
|
||||
"- Detail one",
|
||||
"- Detail two",
|
||||
"MD",
|
||||
")",
|
||||
"jq -n --arg status done --arg comment \"$body\" '{status:$status, comment:$comment}' | \\",
|
||||
" curl -sS -X PATCH \"$api/issues/{{context.issueId}}\" \\",
|
||||
" -H \"Authorization: Bearer $PAPERCLIP_API_KEY\" \\",
|
||||
" -H \"X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID\" \\",
|
||||
" -H \"Content-Type: application/json\" \\",
|
||||
" --data-binary @-",
|
||||
"```",
|
||||
"",
|
||||
DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE,
|
||||
].join("\n");
|
||||
|
||||
IMPORTANT: Use \`terminal\` tool with \`curl\` for ALL Paperclip API calls (web_extract and browser cannot access localhost).
|
||||
function renderConditionalSections(template: string, vars: Record<string, unknown>): string {
|
||||
const isTruthy = (key: string) => {
|
||||
if (key === "noTask") return !vars.taskId;
|
||||
const value = vars[key];
|
||||
if (Array.isArray(value)) return value.length > 0;
|
||||
return Boolean(value);
|
||||
};
|
||||
return template.replace(
|
||||
/\{\{#([a-zA-Z0-9_.-]+)\}\}([\s\S]*?)\{\{\/\1\}\}/g,
|
||||
(_match, key: string, body: string) => (isTruthy(key) ? body : ""),
|
||||
);
|
||||
}
|
||||
|
||||
Your Paperclip identity:
|
||||
Agent ID: {{agentId}}
|
||||
Company ID: {{companyId}}
|
||||
API Base: {{paperclipApiUrl}}
|
||||
|
||||
{{#taskId}}
|
||||
## Assigned Task
|
||||
|
||||
Issue ID: {{taskId}}
|
||||
Title: {{taskTitle}}
|
||||
|
||||
{{taskBody}}
|
||||
|
||||
## Workflow
|
||||
|
||||
0. First, check out the issue so others know you are working on it:
|
||||
\`curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" "{{paperclipApiUrl}}/issues/{{taskId}}/checkout"\`
|
||||
1. Work on the task using your tools
|
||||
2. When done, mark the issue as completed:
|
||||
\`curl -s -X PATCH -H "Authorization: Bearer $PAPERCLIP_API_KEY" -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" "{{paperclipApiUrl}}/issues/{{taskId}}" -H "Content-Type: application/json" -d '{"status":"done"}'\`
|
||||
3. Post a completion comment on the issue summarizing what you did:
|
||||
\`curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" "{{paperclipApiUrl}}/issues/{{taskId}}/comments" -H "Content-Type: application/json" -d '{"body":"DONE: <your summary here>"}'\`
|
||||
4. If this issue has a parent (check the issue body or comments for references like TRA-XX), post a brief notification on the parent issue so the parent owner knows:
|
||||
\`curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" "{{paperclipApiUrl}}/issues/PARENT_ISSUE_ID/comments" -H "Content-Type: application/json" -d '{"body":"{{agentName}} completed {{taskId}}. Summary: <brief>"}'\`
|
||||
{{/taskId}}
|
||||
|
||||
{{#commentId}}
|
||||
## Comment on This Issue
|
||||
|
||||
Someone commented. Read it:
|
||||
\`curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" "{{paperclipApiUrl}}/issues/{{taskId}}/comments/{{commentId}}"\`
|
||||
|
||||
You do NOT need to be assigned to this issue to comment on it.
|
||||
Address the comment, POST a reply if needed, then continue working.
|
||||
{{/commentId}}
|
||||
|
||||
{{#noTask}}
|
||||
## Heartbeat Wake — Check for Work
|
||||
|
||||
First, check your environment for context:
|
||||
\`echo "TASK_ID=$PAPERCLIP_TASK_ID WAKE_REASON=$PAPERCLIP_WAKE_REASON WAKE_COMMENT_ID=$PAPERCLIP_WAKE_COMMENT_ID"\`
|
||||
|
||||
1. List ALL open issues assigned to you (todo, backlog, in_progress):
|
||||
\`curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" "{{paperclipApiUrl}}/companies/{{companyId}}/issues?assigneeAgentId={{agentId}}" | python3 -c "import sys,json;issues=json.loads(sys.stdin.read());[print(f\\"{i['identifier']} {i['status']:>12} {i['priority']:>6} {i['title']}\\") for i in issues if i['status'] not in ('done','cancelled')]" \`
|
||||
|
||||
2. If issues found, pick the highest priority one that is not done/cancelled and work on it:
|
||||
- Read the issue details: \`curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" "{{paperclipApiUrl}}/issues/ISSUE_ID"\`
|
||||
- Do the work in the project directory: {{projectName}}
|
||||
- When done, mark complete and post a comment (see Workflow steps 2-4 above)
|
||||
|
||||
3. If no issues assigned to you, check for unassigned issues:
|
||||
\`curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" "{{paperclipApiUrl}}/companies/{{companyId}}/issues?status=backlog" | python3 -c "import sys,json;issues=json.loads(sys.stdin.read());[print(f\\"{i['identifier']} {i['title']}\\") for i in issues if not i.get('assigneeAgentId')]" \`
|
||||
If you find a relevant issue, assign it to yourself:
|
||||
\`curl -s -X PATCH -H "Authorization: Bearer $PAPERCLIP_API_KEY" -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" "{{paperclipApiUrl}}/issues/ISSUE_ID" -H "Content-Type: application/json" -d '{"assigneeAgentId":"{{agentId}}","status":"todo"}'\`
|
||||
|
||||
4. If truly nothing to do, report briefly what you checked.
|
||||
{{/noTask}}`;
|
||||
|
||||
function buildPrompt(
|
||||
export function buildPrompt(
|
||||
ctx: AdapterExecutionContext,
|
||||
config: Record<string, unknown>,
|
||||
options: { resumedSession?: boolean } = {},
|
||||
): string {
|
||||
const template = cfgString(config.promptTemplate) || DEFAULT_PROMPT_TEMPLATE;
|
||||
const template = cfgString(config.promptTemplate) || HERMES_DEFAULT_PROMPT_TEMPLATE;
|
||||
|
||||
// BUG FIX: Read task/comment data from ctx.context (wake context), not ctx.config (adapter config).
|
||||
// ctx.config only contains adapterConfig (model, provider, timeout); ctx.context has the wake payload.
|
||||
const context = (ctx as any).context || {};
|
||||
const taskId = cfgString(context.taskId) || cfgString(context.issueId) || cfgString(ctx.config?.taskId);
|
||||
const taskTitle = cfgString(context.taskTitle) || cfgString(ctx.config?.taskTitle) || "";
|
||||
@@ -164,12 +157,23 @@ function buildPrompt(
|
||||
paperclipApiUrl = paperclipApiUrl.replace(/\/+$/, "") + "/api";
|
||||
}
|
||||
|
||||
const wakePrompt = renderPaperclipWakePrompt(context.paperclipWake, {
|
||||
resumedSession: options.resumedSession === true,
|
||||
});
|
||||
const paperclipTaskMarkdown = cfgString(context.paperclipTaskMarkdown)?.trim() || "";
|
||||
const sessionHandoffMarkdown = cfgString(context.paperclipSessionHandoffMarkdown)?.trim() || "";
|
||||
const wakePayloadJson = stringifyPaperclipWakePayload(context.paperclipWake) || "";
|
||||
|
||||
const vars: Record<string, unknown> = {
|
||||
agentId: ctx.agent?.id || "",
|
||||
agentName,
|
||||
companyId: ctx.agent?.companyId || "",
|
||||
companyName,
|
||||
runId: ctx.runId || "",
|
||||
agent: ctx.agent || {},
|
||||
company: { id: ctx.agent?.companyId || "", name: companyName },
|
||||
run: { id: ctx.runId || "", source: "on_demand" },
|
||||
context,
|
||||
taskId: taskId || "",
|
||||
taskTitle,
|
||||
taskBody,
|
||||
@@ -177,31 +181,22 @@ function buildPrompt(
|
||||
wakeReason,
|
||||
projectName,
|
||||
paperclipApiUrl,
|
||||
paperclipWakePrompt: wakePrompt,
|
||||
paperclipTaskMarkdown,
|
||||
taskContext: paperclipTaskMarkdown,
|
||||
paperclipWakeJson: wakePayloadJson,
|
||||
wakePayloadJson,
|
||||
paperclipApiKeyEnv: "PAPERCLIP_API_KEY",
|
||||
paperclipRunIdEnv: "PAPERCLIP_RUN_ID",
|
||||
};
|
||||
|
||||
// Handle conditional sections: {{#key}}...{{/key}}
|
||||
let rendered = template;
|
||||
|
||||
// {{#taskId}}...{{/taskId}} — include if task is assigned
|
||||
rendered = rendered.replace(
|
||||
/\{\{#taskId\}\}([\s\S]*?)\{\{\/taskId\}\}/g,
|
||||
taskId ? "$1" : "",
|
||||
);
|
||||
|
||||
// {{#noTask}}...{{/noTask}} — include if no task
|
||||
rendered = rendered.replace(
|
||||
/\{\{#noTask\}\}([\s\S]*?)\{\{\/noTask\}\}/g,
|
||||
taskId ? "" : "$1",
|
||||
);
|
||||
|
||||
// {{#commentId}}...{{/commentId}} — include if comment exists
|
||||
rendered = rendered.replace(
|
||||
/\{\{#commentId\}\}([\s\S]*?)\{\{\/commentId\}\}/g,
|
||||
commentId ? "$1" : "",
|
||||
);
|
||||
|
||||
// Replace remaining {{variable}} placeholders
|
||||
return renderTemplate(rendered, vars);
|
||||
const rendered = renderTemplate(renderConditionalSections(template, vars), vars);
|
||||
return joinPromptSections([
|
||||
wakePrompt,
|
||||
sessionHandoffMarkdown,
|
||||
paperclipTaskMarkdown,
|
||||
rendered,
|
||||
]);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -341,6 +336,9 @@ export async function execute(
|
||||
const persistSession = cfgBoolean(config.persistSession) !== false;
|
||||
const worktreeMode = cfgBoolean(config.worktreeMode) === true;
|
||||
const checkpoints = cfgBoolean(config.checkpoints) === true;
|
||||
const prevSessionId = cfgString(
|
||||
(ctx.runtime?.sessionParams as Record<string, unknown> | null)?.sessionId,
|
||||
);
|
||||
|
||||
// ── Resolve provider (defense in depth) ────────────────────────────────
|
||||
// Priority chain:
|
||||
@@ -403,7 +401,7 @@ export async function execute(
|
||||
}
|
||||
|
||||
// ── Build prompt ───────────────────────────────────────────────────────
|
||||
let prompt = buildPrompt(ctx, config);
|
||||
let prompt = buildPrompt(ctx, config, { resumedSession: Boolean(prevSessionId) });
|
||||
if (agentInstructions) {
|
||||
prompt = agentInstructions + "\n\n---\n\n" + prompt;
|
||||
}
|
||||
@@ -447,10 +445,6 @@ export async function execute(
|
||||
// system is designed for human-attended interactive sessions.
|
||||
args.push("--yolo");
|
||||
|
||||
// Session resume
|
||||
const prevSessionId = cfgString(
|
||||
(ctx.runtime?.sessionParams as Record<string, unknown> | null)?.sessionId,
|
||||
);
|
||||
if (persistSession && prevSessionId) {
|
||||
args.push("--resume", prevSessionId);
|
||||
}
|
||||
@@ -460,8 +454,10 @@ export async function execute(
|
||||
}
|
||||
|
||||
// ── Build environment ──────────────────────────────────────────────────
|
||||
const userEnv = config.env as Record<string, string> | undefined;
|
||||
const env: Record<string, string> = {
|
||||
...(process.env as Record<string, string>),
|
||||
...(userEnv && typeof userEnv === "object" ? userEnv : {}),
|
||||
...buildPaperclipEnv(ctx.agent),
|
||||
};
|
||||
|
||||
@@ -478,11 +474,8 @@ export async function execute(
|
||||
if (envWakeReason) env.PAPERCLIP_WAKE_REASON = envWakeReason;
|
||||
const envCommentId = cfgString(ctxContext.commentId) || cfgString(ctxContext.wakeCommentId) || cfgString(ctx.config?.commentId);
|
||||
if (envCommentId) env.PAPERCLIP_WAKE_COMMENT_ID = envCommentId;
|
||||
|
||||
const userEnv = config.env as Record<string, string> | undefined;
|
||||
if (userEnv && typeof userEnv === "object") {
|
||||
Object.assign(env, userEnv);
|
||||
}
|
||||
const wakePayloadJson = stringifyPaperclipWakePayload(ctxContext.paperclipWake);
|
||||
if (wakePayloadJson) env.PAPERCLIP_WAKE_PAYLOAD_JSON = wakePayloadJson;
|
||||
|
||||
// ── Resolve working directory ──────────────────────────────────────────
|
||||
const cwd =
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import { buildPrompt } from "./execute.js";
|
||||
|
||||
function baseContext(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
agent: {
|
||||
id: "agent-1",
|
||||
name: "Hermes Engineer",
|
||||
companyId: "company-1",
|
||||
},
|
||||
runId: "run-1",
|
||||
config: {},
|
||||
context: {
|
||||
issueId: "issue-1",
|
||||
paperclipWake: {
|
||||
reason: "issue_assigned",
|
||||
issue: {
|
||||
id: "issue-1",
|
||||
identifier: "PAP-3404",
|
||||
title: "Plan the Hermes prompt update",
|
||||
status: "in_progress",
|
||||
priority: "medium",
|
||||
workMode: "planning",
|
||||
},
|
||||
checkedOutByHarness: true,
|
||||
commentWindow: { requestedCount: 0, includedCount: 0, missingCount: 0 },
|
||||
comments: [],
|
||||
fallbackFetchNeeded: false,
|
||||
},
|
||||
paperclipTaskMarkdown: [
|
||||
"Paperclip task context:",
|
||||
'- Issue: "PAP-3404"',
|
||||
'- Title: "Plan the Hermes prompt update"',
|
||||
"",
|
||||
"Planning mode directive:",
|
||||
"Make the plan only. Do not write code or perform implementation work.",
|
||||
"",
|
||||
"Issue description:",
|
||||
"```text",
|
||||
"Use the wake payload as runtime authority.",
|
||||
"```",
|
||||
].join("\n"),
|
||||
...overrides,
|
||||
},
|
||||
} as any;
|
||||
}
|
||||
|
||||
test("renders scoped planning wake authority before the Hermes default workflow", () => {
|
||||
const prompt = buildPrompt(baseContext(), {
|
||||
paperclipApiUrl: "http://127.0.0.1:3101/api",
|
||||
});
|
||||
|
||||
expect(prompt).toContain("## Paperclip Wake Payload");
|
||||
expect(prompt).toContain("- issue: PAP-3404 Plan the Hermes prompt update");
|
||||
expect(prompt).toContain("- planning directive: Make the plan only. Do not write code or perform implementation work.");
|
||||
expect(prompt).toContain("- checkout: already claimed by the harness for this run");
|
||||
expect(prompt).toContain("The harness already checked out this issue for the current run.");
|
||||
expect(prompt).toContain("Issue description:\n```text\nUse the wake payload as runtime authority.\n```");
|
||||
expect(prompt).toContain("clear final disposition");
|
||||
expect(prompt).toContain("keep `in_progress` only when a live continuation path exists");
|
||||
expect(prompt).not.toContain("check for unassigned issues");
|
||||
expect(prompt).not.toContain("status=backlog");
|
||||
});
|
||||
|
||||
test("renders comment wake batch guidance without defaulting to a full-thread refetch", () => {
|
||||
const prompt = buildPrompt(baseContext({
|
||||
wakeCommentId: "comment-1",
|
||||
paperclipWake: {
|
||||
reason: "issue_commented",
|
||||
issue: {
|
||||
id: "issue-1",
|
||||
identifier: "PAP-3404",
|
||||
title: "Plan the Hermes prompt update",
|
||||
status: "in_progress",
|
||||
priority: "medium",
|
||||
workMode: "standard",
|
||||
},
|
||||
latestCommentId: "comment-1",
|
||||
commentWindow: { requestedCount: 1, includedCount: 1, missingCount: 0 },
|
||||
comments: [{ id: "comment-1", body: "Please tighten the prompt.", createdAt: "2026-06-23T00:00:00.000Z" }],
|
||||
fallbackFetchNeeded: false,
|
||||
},
|
||||
}), {});
|
||||
|
||||
expect(prompt).toContain("Use this inline wake data first before refetching the issue thread.");
|
||||
expect(prompt).toContain("Only fetch the API thread when `fallbackFetchNeeded` is true");
|
||||
expect(prompt).toContain("New comments in order:");
|
||||
expect(prompt).toContain("Please tighten the prompt.");
|
||||
expect(prompt).toContain("- fallback fetch needed: no");
|
||||
});
|
||||
|
||||
test("keeps authoritative parent and ancestor context from task markdown", () => {
|
||||
const prompt = buildPrompt(baseContext({
|
||||
paperclipTaskMarkdown: [
|
||||
"Paperclip task context:",
|
||||
'- Issue: "PAP-3404"',
|
||||
"",
|
||||
"Authoritative parent / ancestor context:",
|
||||
"- Parent: PAP-11724 Optimize prompt traces (in_progress) [medium]",
|
||||
"- Ancestor 2: PAP-11721 Fetch raw traces (done) [medium]",
|
||||
].join("\n"),
|
||||
}), {});
|
||||
|
||||
expect(prompt).toContain("Authoritative parent / ancestor context:");
|
||||
expect(prompt).toContain("- Parent: PAP-11724 Optimize prompt traces (in_progress) [medium]");
|
||||
expect(prompt).not.toContain("check the issue body or comments for references");
|
||||
});
|
||||
|
||||
test("preserves custom prompt templates while exposing runtime and wake variables", () => {
|
||||
const prompt = buildPrompt(baseContext(), {
|
||||
paperclipApiUrl: "http://paperclip.local/api",
|
||||
promptTemplate: [
|
||||
"CUSTOM TEMPLATE",
|
||||
"agent={{agent.name}}",
|
||||
"api={{paperclipApiUrl}}",
|
||||
"task={{paperclipTaskMarkdown}}",
|
||||
"wake={{wakePayloadJson}}",
|
||||
].join("\n"),
|
||||
});
|
||||
|
||||
expect(prompt).toContain("CUSTOM TEMPLATE");
|
||||
expect(prompt).toContain("agent=Hermes Engineer");
|
||||
expect(prompt).toContain("api=http://paperclip.local/api");
|
||||
expect(prompt).toContain("task=Paperclip task context:");
|
||||
expect(prompt).toContain('"reason":"issue_assigned"');
|
||||
expect(prompt).toContain("## Paperclip Wake Payload");
|
||||
});
|
||||
@@ -3140,6 +3140,13 @@ export function buildPaperclipTaskMarkdown(input: {
|
||||
workMode?: string | null;
|
||||
description?: string | null;
|
||||
} | null;
|
||||
ancestors?: Array<{
|
||||
id: string;
|
||||
identifier?: string | null;
|
||||
title?: string | null;
|
||||
status?: string | null;
|
||||
priority?: string | null;
|
||||
}> | null;
|
||||
wakeComment?: {
|
||||
id: string;
|
||||
body: string;
|
||||
@@ -3160,6 +3167,7 @@ export function buildPaperclipTaskMarkdown(input: {
|
||||
return [fence + "text", value, fence].join("\n");
|
||||
};
|
||||
const issue = input.issue;
|
||||
const ancestors = (input.ancestors ?? []).slice(0, 6);
|
||||
const wakeComment = input.wakeComment ?? null;
|
||||
const acceptedPlanContinuation =
|
||||
!wakeComment &&
|
||||
@@ -3212,6 +3220,19 @@ export function buildPaperclipTaskMarkdown(input: {
|
||||
lines.push("", "Issue description:", fenceTaskText(description));
|
||||
}
|
||||
}
|
||||
if (ancestors.length > 0) {
|
||||
lines.push("", "Authoritative parent / ancestor context:");
|
||||
for (const [index, ancestor] of ancestors.entries()) {
|
||||
const label = ancestor.identifier || ancestor.id;
|
||||
const status = ancestor.status ? ` (${ancestor.status})` : "";
|
||||
const priority = ancestor.priority ? ` [${ancestor.priority}]` : "";
|
||||
const title = ancestor.title ? ` ${ancestor.title}` : "";
|
||||
lines.push(`- ${index === 0 ? "Parent" : `Ancestor ${index + 1}`}: ${label}${title}${status}${priority}`);
|
||||
}
|
||||
if ((input.ancestors ?? []).length > ancestors.length) {
|
||||
lines.push(`- [ancestor context truncated after ${ancestors.length} entries]`);
|
||||
}
|
||||
}
|
||||
if (wakeComment?.body.trim()) {
|
||||
lines.push("", "Latest wake comment:", fenceTaskText(wakeComment.body.trim()));
|
||||
}
|
||||
@@ -8647,6 +8668,9 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
wakeCommentContext && !exposeLowTrustRaw
|
||||
? sanitizeQuarantinedCommentForHigherTrust(wakeCommentContext)
|
||||
: wakeCommentContext;
|
||||
const issueAncestors = issueRef
|
||||
? await issuesSvc.getAncestors(issueRef.id)
|
||||
: [];
|
||||
if (continuationSummary) {
|
||||
context.paperclipContinuationSummary = {
|
||||
key: safeContinuationSummary!.key,
|
||||
@@ -8692,6 +8716,7 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
description: issueRef.description,
|
||||
}
|
||||
: null,
|
||||
ancestors: issueAncestors,
|
||||
wakeComment: safeWakeCommentContext,
|
||||
interaction: {
|
||||
kind: readNonEmptyString(context.interactionKind),
|
||||
|
||||
Reference in new issue
Block a user