Skip to content

Commit 8d9a8ba

Browse files
feat: bump memory plugin to v0.0.21 with plan approval tests
1 parent 1248d0b commit 8d9a8ba

3 files changed

Lines changed: 165 additions & 8 deletions

File tree

packages/memory/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opencode-manager/memory",
3-
"version": "0.0.20",
3+
"version": "0.0.21",
44
"type": "module",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

packages/memory/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,9 +1304,10 @@ Do NOT output text without also making this tool call.
13041304
const labels = options.map((o) => o.label)
13051305
const isPlanApproval = PLAN_APPROVAL_LABELS.every((l) => labels.includes(l))
13061306
if (isPlanApproval) {
1307-
const answer = output.output.trim()
1307+
const metadata = output.metadata as { answers?: string[][] } | undefined
1308+
const answer = metadata?.answers?.[0]?.[0]?.trim() ?? output.output.trim()
13081309
const matchedLabel = PLAN_APPROVAL_LABELS.find((l) => answer === l || answer.startsWith(l))
1309-
const directive = matchedLabel ? PLAN_APPROVAL_DIRECTIVES[matchedLabel] : '<system-reminder>\nThe user cancelled or provided a custom response. Do NOT call memory-plan-execute or memory-plan-ralph.\n</system-reminder>'
1310+
const directive = matchedLabel ? PLAN_APPROVAL_DIRECTIVES[matchedLabel] : '<system-reminder>\nThe user provided a custom response instead of selecting a predefined option. Review their answer and respond accordingly. If they want to proceed with execution, use the appropriate tool (memory-plan-execute or memory-plan-ralph) based on their intent. If they want to cancel or revise the plan, help them with that instead.\n</system-reminder>'
13101311
output.output = `${output.output}\n\n${directive}`
13111312
logger.log(`Plan approval: detected "${matchedLabel ?? 'cancel/custom'}" answer, injected directive`)
13121313
}

packages/memory/test/plan-approval.test.ts

Lines changed: 161 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Do NOT output text without also making this tool call.
7070
</system-reminder>`,
7171
}
7272

73-
const CANCEL_DIRECTIVE = '<system-reminder>\nThe user cancelled or provided a custom response. Do NOT call memory-plan-execute or memory-plan-ralph.\n</system-reminder>'
73+
const CANCEL_DIRECTIVE = '<system-reminder>\nThe user provided a custom response instead of selecting a predefined option. Review their answer and respond accordingly. If they want to proceed with execution, use the appropriate tool (memory-plan-execute or memory-plan-ralph) based on their intent. If they want to cancel or revise the plan, help them with that instead.\n</system-reminder>'
7474

7575
beforeEach(() => {
7676
db = createTestDb()
@@ -117,7 +117,8 @@ Do NOT output text without also making this tool call.
117117
const labels = options.map((o) => o.label)
118118
const isPlanApproval = PLAN_APPROVAL_LABELS.every((l) => labels.includes(l))
119119
if (isPlanApproval) {
120-
const answer = output.output.trim()
120+
const metadata = output.metadata as { answers?: string[][] } | undefined
121+
const answer = metadata?.answers?.[0]?.[0]?.trim() ?? output.output.trim()
121122
const matchedLabel = PLAN_APPROVAL_LABELS.find((l) => answer === l || answer.startsWith(l))
122123
const directive = matchedLabel ? PLAN_APPROVAL_DIRECTIVES[matchedLabel] : CANCEL_DIRECTIVE
123124
output.output = `${output.output}\n\n${directive}`
@@ -246,8 +247,8 @@ Do NOT output text without also making this tool call.
246247

247248
expect(output.output).toContain('Custom answer')
248249
expect(output.output).toContain('<system-reminder>')
249-
expect(output.output).toContain('cancelled or provided a custom response')
250-
expect(output.output).toContain('Do NOT call memory-plan-execute or memory-plan-ralph')
250+
expect(output.output).toContain('custom response')
251+
expect(output.output).toContain('respond accordingly')
251252
})
252253

253254
test('Matches partial answer that starts with label', () => {
@@ -289,7 +290,7 @@ Do NOT output text without also making this tool call.
289290

290291
expect(output.output).toContain('I want to create a session')
291292
expect(output.output).toContain('<system-reminder>')
292-
expect(output.output).toContain('cancelled or provided a custom response')
293+
expect(output.output).toContain('custom response')
293294
})
294295

295296
test('Does not modify non-approval questions', () => {
@@ -364,4 +365,159 @@ Do NOT output text without also making this tool call.
364365
expect(output.title).toBe('')
365366
expect(output.output).toBe('test')
366367
})
368+
369+
test('Detects plan approval using metadata.answers when output is full sentence', () => {
370+
const args = {
371+
questions: [{
372+
question: 'How would you like to proceed?',
373+
options: [
374+
{ label: 'New session', description: 'Create new session' },
375+
{ label: 'Execute here', description: 'Execute here' },
376+
{ label: 'Ralph (worktree)', description: 'Ralph worktree' },
377+
{ label: 'Ralph (in place)', description: 'Ralph in place' },
378+
],
379+
}],
380+
}
381+
const output = {
382+
title: 'Asked 1 question',
383+
output: 'User has answered your questions: "How would you like to proceed?"="Ralph (worktree)". You can now continue with the user\'s answers in mind.',
384+
metadata: { answers: [['Ralph (worktree)']] },
385+
}
386+
387+
simulateToolExecuteAfter('question', args, output)
388+
389+
expect(output.output).toContain('Ralph (worktree)')
390+
expect(output.output).toContain('<system-reminder>')
391+
expect(output.output).toContain('memory-plan-ralph')
392+
expect(output.output).toContain('inPlace: false')
393+
})
394+
395+
test('Detects "Ralph (in place)" using metadata.answers when output is full sentence', () => {
396+
const args = {
397+
questions: [{
398+
question: 'How would you like to proceed?',
399+
options: [
400+
{ label: 'New session', description: 'Create new session' },
401+
{ label: 'Execute here', description: 'Execute here' },
402+
{ label: 'Ralph (worktree)', description: 'Ralph worktree' },
403+
{ label: 'Ralph (in place)', description: 'Ralph in place' },
404+
],
405+
}],
406+
}
407+
const output = {
408+
title: 'Asked 1 question',
409+
output: 'User has answered your questions: "How would you like to proceed?"="Ralph (in place)". You can now continue with the user\'s answers in mind.',
410+
metadata: { answers: [['Ralph (in place)']] },
411+
}
412+
413+
simulateToolExecuteAfter('question', args, output)
414+
415+
expect(output.output).toContain('Ralph (in place)')
416+
expect(output.output).toContain('<system-reminder>')
417+
expect(output.output).toContain('memory-plan-ralph')
418+
expect(output.output).toContain('inPlace: true')
419+
})
420+
421+
test('Detects "New session" using metadata.answers when output is full sentence', () => {
422+
const args = {
423+
questions: [{
424+
question: 'How would you like to proceed?',
425+
options: [
426+
{ label: 'New session', description: 'Create new session' },
427+
{ label: 'Execute here', description: 'Execute here' },
428+
{ label: 'Ralph (worktree)', description: 'Ralph worktree' },
429+
{ label: 'Ralph (in place)', description: 'Ralph in place' },
430+
],
431+
}],
432+
}
433+
const output = {
434+
title: 'Asked 1 question',
435+
output: 'User has answered your questions: "How would you like to proceed?"="New session". You can now continue with the user\'s answers in mind.',
436+
metadata: { answers: [['New session']] },
437+
}
438+
439+
simulateToolExecuteAfter('question', args, output)
440+
441+
expect(output.output).toContain('New session')
442+
expect(output.output).toContain('<system-reminder>')
443+
expect(output.output).toContain('memory-plan-execute')
444+
expect(output.output).toContain('inPlace: false')
445+
})
446+
447+
test('Detects "Execute here" using metadata.answers when output is full sentence', () => {
448+
const args = {
449+
questions: [{
450+
question: 'How would you like to proceed?',
451+
options: [
452+
{ label: 'New session', description: 'Create new session' },
453+
{ label: 'Execute here', description: 'Execute here' },
454+
{ label: 'Ralph (worktree)', description: 'Ralph worktree' },
455+
{ label: 'Ralph (in place)', description: 'Ralph in place' },
456+
],
457+
}],
458+
}
459+
const output = {
460+
title: 'Asked 1 question',
461+
output: 'User has answered your questions: "How would you like to proceed?"="Execute here". You can now continue with the user\'s answers in mind.',
462+
metadata: { answers: [['Execute here']] },
463+
}
464+
465+
simulateToolExecuteAfter('question', args, output)
466+
467+
expect(output.output).toContain('Execute here')
468+
expect(output.output).toContain('<system-reminder>')
469+
expect(output.output).toContain('memory-plan-execute')
470+
expect(output.output).toContain('inPlace: true')
471+
})
472+
473+
test('Falls back to output.output when metadata.answers is missing', () => {
474+
const args = {
475+
questions: [{
476+
question: 'How would you like to proceed?',
477+
options: [
478+
{ label: 'New session', description: 'Create new session' },
479+
{ label: 'Execute here', description: 'Execute here' },
480+
{ label: 'Ralph (worktree)', description: 'Ralph worktree' },
481+
{ label: 'Ralph (in place)', description: 'Ralph in place' },
482+
],
483+
}],
484+
}
485+
const output = {
486+
title: 'Asked 1 question',
487+
output: 'New session',
488+
metadata: {},
489+
}
490+
491+
simulateToolExecuteAfter('question', args, output)
492+
493+
expect(output.output).toContain('New session')
494+
expect(output.output).toContain('<system-reminder>')
495+
expect(output.output).toContain('memory-plan-execute')
496+
expect(output.output).toContain('inPlace: false')
497+
})
498+
499+
test('Injects cancel directive when metadata.answers contains unknown label', () => {
500+
const args = {
501+
questions: [{
502+
question: 'How would you like to proceed?',
503+
options: [
504+
{ label: 'New session', description: 'Create new session' },
505+
{ label: 'Execute here', description: 'Execute here' },
506+
{ label: 'Ralph (worktree)', description: 'Ralph worktree' },
507+
{ label: 'Ralph (in place)', description: 'Ralph in place' },
508+
],
509+
}],
510+
}
511+
const output = {
512+
title: 'Asked 1 question',
513+
output: 'User has answered your questions: "How would you like to proceed?"="Some custom answer". You can now continue with the user\'s answers in mind.',
514+
metadata: { answers: [['Some custom answer']] },
515+
}
516+
517+
simulateToolExecuteAfter('question', args, output)
518+
519+
expect(output.output).toContain('<system-reminder>')
520+
expect(output.output).toContain('custom response')
521+
expect(output.output).toContain('respond accordingly')
522+
})
367523
})

0 commit comments

Comments
 (0)