@linimin/pi-letscook 0.1.54 → 0.1.56
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/CHANGELOG.md +3 -2
- package/README.md +15 -17
- package/extensions/completion/driver.ts +44 -110
- package/extensions/completion/index.ts +54 -89
- package/extensions/completion/prompt-surfaces.ts +65 -380
- package/extensions/completion/proposal.ts +5 -65
- package/extensions/completion/role-runner.ts +4 -311
- package/extensions/completion/state-store.ts +212 -5
- package/extensions/completion/transcription.ts +0 -8
- package/extensions/completion/types.ts +0 -114
- package/package.json +15 -4
- package/scripts/active-slice-contract-test.sh +61 -6
- package/scripts/context-proposal-test.sh +33 -29
- package/scripts/legacy-cleanup-test.sh +11 -0
- package/scripts/refocus-test.sh +10 -11
- package/scripts/release-check.sh +15 -12
- package/scripts/role-runner-contract-test.sh +1 -2
- package/scripts/rubric-contract-test.sh +0 -1
- package/scripts/smoke-test.sh +24 -13
- package/skills/cook-handoff-boundary/SKILL.md +64 -0
- package/extensions/completion/input-routing.ts +0 -819
- package/scripts/cook-trigger-routing-test.sh +0 -1122
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import * as fs from "node:fs";
|
|
3
2
|
import { promises as fsp } from "node:fs";
|
|
4
3
|
import * as os from "node:os";
|
|
@@ -39,9 +38,9 @@ import {
|
|
|
39
38
|
buildContextProposalConfirmationLayout as buildExtractedContextProposalConfirmationLayout,
|
|
40
39
|
buildContextProposalConfirmationSelectItems,
|
|
41
40
|
buildContextProposalContinuationReason as buildExtractedContextProposalContinuationReason,
|
|
41
|
+
buildCookHandoffBoundaryReminder as buildExtractedCookHandoffBoundaryReminder,
|
|
42
42
|
buildEvaluationRoleContextLines as buildExtractedEvaluationRoleContextLines,
|
|
43
43
|
buildEvaluationRoleReminderText as buildExtractedEvaluationRoleReminderText,
|
|
44
|
-
buildNaturalLanguageHandoffMetadataLines,
|
|
45
44
|
buildResumeCapsule as buildExtractedResumeCapsule,
|
|
46
45
|
buildSystemReminder as buildExtractedSystemReminder,
|
|
47
46
|
maybeWriteContextProposalConfirmationSnapshot,
|
|
@@ -76,11 +75,9 @@ import {
|
|
|
76
75
|
readText,
|
|
77
76
|
scaffoldCompletionFiles as scaffoldCompletionFilesOnDisk,
|
|
78
77
|
} from "./state-store";
|
|
79
|
-
import { parseFirstNumber, parseYesNo } from "./transcription";
|
|
80
78
|
import type { TranscriptionResult } from "./transcription";
|
|
81
|
-
import type { CompletionStateSnapshot, CompletionRole,
|
|
79
|
+
import type { CompletionStateSnapshot, CompletionRole, JsonRecord, LiveRoleActivity } from "./types";
|
|
82
80
|
|
|
83
|
-
const PROTOCOL_ID = "completion";
|
|
84
81
|
const ROLE_NAMES = [
|
|
85
82
|
"completion-bootstrapper",
|
|
86
83
|
"completion-regrounder",
|
|
@@ -123,10 +120,6 @@ function candidateSlices(plan: JsonRecord | undefined): JsonRecord[] {
|
|
|
123
120
|
return Array.isArray(slices) ? slices.filter(isRecord) : [];
|
|
124
121
|
}
|
|
125
122
|
|
|
126
|
-
type ExistingWorkflowDecision =
|
|
127
|
-
| { action: "continue"; currentMissionAnchor: string }
|
|
128
|
-
| { action: "refocus"; currentMissionAnchor: string; missionAnchor: string };
|
|
129
|
-
|
|
130
123
|
type ActiveWorkflowProposalAssessment = {
|
|
131
124
|
action: "continue" | "refocus" | "unclear";
|
|
132
125
|
currentMissionAnchor: string;
|
|
@@ -134,13 +127,6 @@ type ActiveWorkflowProposalAssessment = {
|
|
|
134
127
|
reason: "matching_mission" | "clear_refocus" | "missing_proposal" | "ambiguous_discussion";
|
|
135
128
|
};
|
|
136
129
|
|
|
137
|
-
type ExistingWorkflowChooserOptions = {
|
|
138
|
-
intro?: string;
|
|
139
|
-
proposedMissionLabel?: string;
|
|
140
|
-
refocusChoiceLabel?: string;
|
|
141
|
-
comparison?: "semantic" | "strict";
|
|
142
|
-
};
|
|
143
|
-
|
|
144
130
|
function completionTestWorkflowActionOverride(): "continue" | "refocus" | "cancel" | undefined {
|
|
145
131
|
const raw = process.env.PI_COMPLETION_EXISTING_WORKFLOW_ACTION?.trim().toLowerCase();
|
|
146
132
|
return raw === "continue" || raw === "refocus" || raw === "cancel" ? raw : undefined;
|
|
@@ -196,6 +182,10 @@ function completionTestSystemReminderPath(): string | undefined {
|
|
|
196
182
|
return asString(process.env.PI_COMPLETION_TEST_SYSTEM_REMINDER_PATH);
|
|
197
183
|
}
|
|
198
184
|
|
|
185
|
+
function completionTestCookHandoffReminderPath(): string | undefined {
|
|
186
|
+
return asString(process.env.PI_COMPLETION_TEST_COOK_HANDOFF_REMINDER_PATH);
|
|
187
|
+
}
|
|
188
|
+
|
|
199
189
|
function maybeWriteTestSnapshot(targetPath: string | undefined, content: string): void {
|
|
200
190
|
if (!targetPath) return;
|
|
201
191
|
try {
|
|
@@ -207,26 +197,8 @@ function maybeWriteTestSnapshot(targetPath: string | undefined, content: string)
|
|
|
207
197
|
}
|
|
208
198
|
|
|
209
199
|
const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and rerun /cook.";
|
|
210
|
-
const COOK_BARE_ONLY_GUIDANCE =
|
|
211
|
-
"/cook is the canonical workflow boundary. Discuss the concrete repo changes in the main chat, then run /cook when you want to start, continue, refocus, or begin the next workflow round.";
|
|
212
200
|
const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
|
|
213
|
-
"/cook failed closed because recent discussion did not produce a clear execution-ready Mission/Scope/Constraints/Acceptance
|
|
214
|
-
|
|
215
|
-
function buildCookCancellationMessage(prefix: string): string {
|
|
216
|
-
return `${prefix}. ${COOK_MAIN_CHAT_RERUN_GUIDANCE}`;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function buildCookStructuredDiscussionFailureMessage(prefix?: string): string {
|
|
220
|
-
return prefix ? `${prefix} ${COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL}` : COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function shouldDisableContextProposalAnalyst(): boolean {
|
|
224
|
-
return process.env.PI_COMPLETION_DISABLE_CONTEXT_PROPOSAL_ANALYST === "1";
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function completionTestContextProposalAnalystOutput(): string | undefined {
|
|
228
|
-
return asString(process.env.PI_COMPLETION_CONTEXT_PROPOSAL_ANALYST_OUTPUT);
|
|
229
|
-
}
|
|
201
|
+
"/cook failed closed because recent discussion did not produce a clear execution-ready startup brief with Mission/Scope/Constraints/Acceptance for concrete repo changes. Clarify the concrete repo changes in the main chat and rerun /cook.";
|
|
230
202
|
|
|
231
203
|
function isWorkflowDone(snapshot: CompletionStateSnapshot | undefined): boolean {
|
|
232
204
|
return asString(snapshot?.state?.continuation_policy) === "done";
|
|
@@ -258,6 +230,19 @@ function shouldInjectCompletionWorkflowContext(snapshot: CompletionStateSnapshot
|
|
|
258
230
|
return hasCompletionRoutingActivation(snapshot) && isCompletionDriverPromptTurn(ctx);
|
|
259
231
|
}
|
|
260
232
|
|
|
233
|
+
function shouldInjectCookHandoffBoundary(event: { prompt?: string }, ctx: { sessionManager?: any }): boolean {
|
|
234
|
+
if (roleFromEnv()) return false;
|
|
235
|
+
if (isCompletionDriverPromptTurn(ctx)) return false;
|
|
236
|
+
const prompt = typeof event.prompt === "string" ? event.prompt.trim() : "";
|
|
237
|
+
if (!prompt) return false;
|
|
238
|
+
if (prompt.startsWith("/")) return false;
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function buildCookHandoffBoundaryReminder(): string {
|
|
243
|
+
return buildExtractedCookHandoffBoundaryReminder();
|
|
244
|
+
}
|
|
245
|
+
|
|
261
246
|
function buildDoneWorkflowBoundaryReminder(snapshot: CompletionStateSnapshot): string {
|
|
262
247
|
const missionAnchor = asString(snapshot.state?.mission_anchor) ?? asString(snapshot.plan?.mission_anchor) ?? "(unknown)";
|
|
263
248
|
const continuationReason = asString(snapshot.state?.continuation_reason) ?? "(unknown)";
|
|
@@ -378,7 +363,6 @@ async function promptContextProposalConfirmationAction(
|
|
|
378
363
|
async function deriveCookContextProposal(
|
|
379
364
|
ctx: { cwd: string; hasUI: boolean; ui: any; sessionManager: any; model?: any; modelRegistry?: any },
|
|
380
365
|
projectName: string,
|
|
381
|
-
hintText?: string,
|
|
382
366
|
): Promise<ContextProposal | undefined> {
|
|
383
367
|
const recentEntries = collectRecentDiscussionEntries(ctx, { isRecord, asString, isStaleContextError });
|
|
384
368
|
const snapshot = await loadCompletionSnapshot(getCtxCwd(ctx));
|
|
@@ -394,11 +378,9 @@ async function deriveCookContextProposal(
|
|
|
394
378
|
`verification summary: ${asString(snapshot.verificationEvidence?.summary) ?? "(none)"}`,
|
|
395
379
|
]
|
|
396
380
|
: [];
|
|
397
|
-
if (hintText) workflowContextLines.push(`cook hint: ${hintText}`);
|
|
398
381
|
return await deriveCookContextProposalFromRecentDiscussion(projectName, recentEntries, {
|
|
399
382
|
asString,
|
|
400
383
|
asStringArray,
|
|
401
|
-
hintText,
|
|
402
384
|
workflowContext: snapshot
|
|
403
385
|
? {
|
|
404
386
|
currentMissionAnchor:
|
|
@@ -412,15 +394,12 @@ async function deriveCookContextProposal(
|
|
|
412
394
|
continuationPolicy: asString(snapshot.state?.continuation_policy),
|
|
413
395
|
}
|
|
414
396
|
: undefined,
|
|
415
|
-
analyzeContextProposal: async (entries
|
|
397
|
+
analyzeContextProposal: async (entries) =>
|
|
416
398
|
await analyzeContextProposalWithAgent({
|
|
417
399
|
ctx,
|
|
418
400
|
projectName,
|
|
419
401
|
recentEntries: entries,
|
|
420
|
-
workflowContextLines
|
|
421
|
-
derivedHintText && !workflowContextLines.includes(`cook hint: ${derivedHintText}`)
|
|
422
|
-
? [...workflowContextLines, `cook hint: ${derivedHintText}`]
|
|
423
|
-
: workflowContextLines,
|
|
402
|
+
workflowContextLines,
|
|
424
403
|
liveRoleActivityByRoot,
|
|
425
404
|
completionStatusKey: COMPLETION_STATUS_KEY,
|
|
426
405
|
safeUiCall,
|
|
@@ -470,12 +449,13 @@ async function confirmContextProposal(
|
|
|
470
449
|
async function scaffoldCompletionFiles(
|
|
471
450
|
root: string,
|
|
472
451
|
missionAnchor: string,
|
|
473
|
-
options?: { analysis?: ContextProposalAnalysis; continuationReason?: string },
|
|
452
|
+
options?: { analysis?: ContextProposalAnalysis; continuationReason?: string; advisoryStartupBrief?: JsonRecord },
|
|
474
453
|
) {
|
|
475
454
|
const routing = finalizeContextProposalAnalysis(options?.analysis, [missionAnchor]);
|
|
476
455
|
return await scaffoldCompletionFilesOnDisk(root, missionAnchor, {
|
|
477
456
|
analysis: { taskType: routing.taskType, evaluationProfile: routing.evaluationProfile },
|
|
478
457
|
continuationReason: options?.continuationReason,
|
|
458
|
+
advisoryStartupBrief: options?.advisoryStartupBrief,
|
|
479
459
|
});
|
|
480
460
|
}
|
|
481
461
|
|
|
@@ -875,45 +855,24 @@ function composeResumeCapsule(snapshot: CompletionStateSnapshot, sliceHistory: J
|
|
|
875
855
|
});
|
|
876
856
|
}
|
|
877
857
|
|
|
878
|
-
async function gitHeadSha(cwd: string): Promise<string | undefined> {
|
|
879
|
-
return await new Promise((resolve) => {
|
|
880
|
-
const proc = spawn("git", ["rev-parse", "HEAD"], { cwd, stdio: ["ignore", "pipe", "ignore"] });
|
|
881
|
-
let stdout = "";
|
|
882
|
-
proc.stdout.on("data", (chunk) => {
|
|
883
|
-
stdout += chunk.toString();
|
|
884
|
-
});
|
|
885
|
-
proc.on("close", (code) => {
|
|
886
|
-
resolve(code === 0 ? asString(stdout) : undefined);
|
|
887
|
-
});
|
|
888
|
-
proc.on("error", () => resolve(undefined));
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
|
|
892
858
|
function completionKickoff(
|
|
893
859
|
goal: string,
|
|
894
860
|
taskType: string,
|
|
895
861
|
evaluationProfile: string,
|
|
896
862
|
intent: "auto" | "continue" | "refocus" = "auto",
|
|
897
863
|
missionAnchor?: string,
|
|
898
|
-
naturalLanguageHandoff?: CookNaturalLanguageHandoff,
|
|
899
864
|
): string {
|
|
900
|
-
const naturalLanguageHandoffBlock = buildNaturalLanguageHandoffMetadataLines(naturalLanguageHandoff).join("\n");
|
|
901
865
|
const intentBlock =
|
|
902
866
|
intent === "continue" && missionAnchor
|
|
903
867
|
? `Existing canonical mission anchor:\n${missionAnchor}\n\nWorkflow intent:\n- Continue the existing workflow.\n- Treat the new user text as supplemental direction unless canonical reconciliation proves the mission itself must change.\n\n`
|
|
904
868
|
: intent === "refocus" && missionAnchor
|
|
905
869
|
? `Updated canonical mission anchor:\n${missionAnchor}\n\nWorkflow intent:\n- The user explicitly refocused the workflow before this kickoff.\n- Re-read canonical .agent/** state and continue from the refocused mission.\n\n`
|
|
906
870
|
: "";
|
|
907
|
-
return `/skill:completion-protocol Start or continue the completion workflow for this repo.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\nUser goal:\n${goal}\n\n${
|
|
871
|
+
return `/skill:completion-protocol Start or continue the completion workflow for this repo.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\nUser goal:\n${goal}\n\n${intentBlock}Driver instructions:\n- Canonical truth is in .agent/**. Re-read .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting when they exist.\n- If tracked completion contract files are missing or onboarding is required, invoke completion_role with role completion-bootstrapper.\n- Otherwise follow the mandatory dispatch rules from completion-protocol.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Use completion_role for all completion-* role work. Do not directly implement tracked product changes yourself.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
|
|
908
872
|
}
|
|
909
873
|
|
|
910
|
-
function completionResumePrompt(
|
|
911
|
-
taskType:
|
|
912
|
-
evaluationProfile: string,
|
|
913
|
-
naturalLanguageHandoff?: CookNaturalLanguageHandoff,
|
|
914
|
-
): string {
|
|
915
|
-
const naturalLanguageHandoffBlock = buildNaturalLanguageHandoffMetadataLines(naturalLanguageHandoff).join("\n");
|
|
916
|
-
return `/skill:completion-protocol Resume the completion workflow from canonical state.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\n${naturalLanguageHandoffBlock}Resume instructions:\n- Re-read .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting.\n- If canonical state is missing, invalid, contradictory, stale, or ambiguous, route to completion-regrounder first.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Continue from next_mandatory_role and next_mandatory_action.\n- Use completion_role for all completion-* role work.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
|
|
874
|
+
function completionResumePrompt(taskType: string, evaluationProfile: string): string {
|
|
875
|
+
return `/skill:completion-protocol Resume the completion workflow from canonical state.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\nResume instructions:\n- Re-read .agent/state.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting.\n- If canonical state is missing, invalid, contradictory, stale, or ambiguous, route to completion-regrounder first.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Continue from next_mandatory_role and next_mandatory_action.\n- Use completion_role for all completion-* role work.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
|
|
917
876
|
}
|
|
918
877
|
|
|
919
878
|
export default function completionExtension(pi: ExtensionAPI) {
|
|
@@ -926,11 +885,10 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
926
885
|
getCtxUi,
|
|
927
886
|
};
|
|
928
887
|
const driverDeps = {
|
|
929
|
-
bareOnlyGuidance: COOK_BARE_ONLY_GUIDANCE,
|
|
930
888
|
structuredDiscussionFailureDetail: COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL,
|
|
931
889
|
mainChatRerunGuidance: COOK_MAIN_CHAT_RERUN_GUIDANCE,
|
|
932
890
|
cookCommandSpec: {
|
|
933
|
-
description: "/cook workflow: start, continue, refocus, or start the next round from
|
|
891
|
+
description: "/cook workflow: derive a startup brief from recent discussion, then start, continue, refocus, or start the next round from the explicit /cook command",
|
|
934
892
|
},
|
|
935
893
|
buildContextProposalContinuationReason,
|
|
936
894
|
completionKickoff,
|
|
@@ -986,7 +944,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
986
944
|
}
|
|
987
945
|
});
|
|
988
946
|
|
|
989
|
-
pi.on("before_agent_start", async (
|
|
947
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
990
948
|
const loaded = await loadCompletionDataForReminder(getCtxCwd(ctx));
|
|
991
949
|
const driverPromptTurn = isCompletionDriverPromptTurn(ctx);
|
|
992
950
|
if (loaded && driverPromptTurn) {
|
|
@@ -994,28 +952,35 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
994
952
|
const fingerprint = completionContinuationFingerprint(loaded.snapshot);
|
|
995
953
|
if (fingerprint) markQueuedDriverPromptInFlight(rootKey, fingerprint);
|
|
996
954
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
955
|
+
const systemPrompt = getSystemPromptSafe(ctx);
|
|
956
|
+
if (!systemPrompt) return;
|
|
957
|
+
if (loaded && shouldInjectCompletionWorkflowContext(loaded.snapshot, ctx)) {
|
|
958
|
+
const additions = isWorkflowDone(loaded.snapshot)
|
|
959
|
+
? [buildDoneWorkflowBoundaryReminder(loaded.snapshot)]
|
|
960
|
+
: [composeSystemReminder(loaded.snapshot, loaded.sliceHistory, loaded.stopHistory)];
|
|
961
|
+
if (!isWorkflowDone(loaded.snapshot)) {
|
|
962
|
+
const markerText = await readText(loaded.snapshot.files.compactionMarkerPath);
|
|
963
|
+
let marker: JsonRecord | undefined;
|
|
964
|
+
if (markerText) {
|
|
965
|
+
try {
|
|
966
|
+
const parsed = JSON.parse(markerText);
|
|
967
|
+
marker = isRecord(parsed) ? parsed : undefined;
|
|
968
|
+
} catch {
|
|
969
|
+
marker = undefined;
|
|
970
|
+
}
|
|
1010
971
|
}
|
|
972
|
+
if (marker) additions.push(buildPostCompactionDriverInstructions(loaded.snapshot, marker));
|
|
1011
973
|
}
|
|
1012
|
-
|
|
974
|
+
maybeWriteTestSnapshot(completionTestSystemReminderPath(), additions.join("\n\n"));
|
|
975
|
+
return {
|
|
976
|
+
systemPrompt: `${systemPrompt}\n\n${additions.join("\n\n")}`,
|
|
977
|
+
};
|
|
1013
978
|
}
|
|
1014
|
-
|
|
1015
|
-
const
|
|
1016
|
-
|
|
979
|
+
if (!shouldInjectCookHandoffBoundary(event, ctx)) return;
|
|
980
|
+
const handoffReminder = buildCookHandoffBoundaryReminder();
|
|
981
|
+
maybeWriteTestSnapshot(completionTestCookHandoffReminderPath(), handoffReminder);
|
|
1017
982
|
return {
|
|
1018
|
-
systemPrompt: `${systemPrompt}\n\n${
|
|
983
|
+
systemPrompt: `${systemPrompt}\n\n${handoffReminder}`,
|
|
1019
984
|
};
|
|
1020
985
|
});
|
|
1021
986
|
|