mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
feat(terminal): add "Copy Context" to the terminal context menu (#6267)
* feat(terminal): add "Copy Context" to the terminal context menu Copying an agent session's captured context required opening the Fork Agent Session dialog and clicking its "Copy context" button — two steps for something you often just want on its own (e.g. to paste elsewhere). Add a "Copy Context" item to the terminal right-click menu, right under "Fork Agent Session…". It reuses the existing capture + clipboard path (prepareAgentSessionForkFromPane → copyAgentSessionForkContext), so the copied text is identical to the dialog's, just one click away and without opening the dialog. Fixes #5020 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(terminal): copy plain context, not the fork prompt Copy Context now copies the bounded, cleaned transcript on its own with a neutral 'Context copied' toast, instead of reusing the fork prompt (with its 'this is a fork… acknowledge and wait' framing) and the fork-copy toast. That framing is noise when pasting into another tool, which is the issue's use case. Extracts buildBoundedSessionTranscript() from the fork-prompt builder and adds copyAgentSessionContextFromPane(); the fork dialog's own copy button is unchanged. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
co-authored by
Orca
Claude Opus 4.8
Jinjing
parent
a9ef6f9168
commit
3d22d69d03
@@ -0,0 +1,97 @@
|
||||
import React from 'react'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import TerminalContextMenu from './TerminalContextMenu'
|
||||
|
||||
type ItemProps = { onSelect?: () => void; children?: React.ReactNode }
|
||||
|
||||
const items = vi.hoisted(() => ({ list: [] as ItemProps[] }))
|
||||
|
||||
vi.mock('@/components/ui/dropdown-menu', async () => {
|
||||
const React_ = await import('react')
|
||||
const passthrough = ({ children }: { children?: React.ReactNode }) =>
|
||||
React_.createElement(React_.Fragment, null, children)
|
||||
return {
|
||||
DropdownMenu: passthrough,
|
||||
DropdownMenuContent: passthrough,
|
||||
DropdownMenuLabel: passthrough,
|
||||
DropdownMenuSeparator: () => null,
|
||||
DropdownMenuShortcut: passthrough,
|
||||
DropdownMenuSub: passthrough,
|
||||
DropdownMenuSubContent: passthrough,
|
||||
DropdownMenuSubTrigger: passthrough,
|
||||
DropdownMenuTrigger: passthrough,
|
||||
DropdownMenuItem: (props: ItemProps) => {
|
||||
items.list.push(props)
|
||||
return React.createElement(React.Fragment, null, props.children)
|
||||
}
|
||||
}
|
||||
})
|
||||
vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback }))
|
||||
vi.mock('@/lib/agent-catalog', () => ({ AgentIcon: () => null }))
|
||||
vi.mock('@/hooks/useShortcutLabel', () => ({ formatShortcutLabel: () => 'X' }))
|
||||
vi.mock('./terminal-context-menu-dismiss', () => ({
|
||||
shouldIgnoreTerminalMenuPointerDownOutside: () => false
|
||||
}))
|
||||
|
||||
function childrenText(children: React.ReactNode): string {
|
||||
return React.Children.toArray(children)
|
||||
.filter((child): child is string => typeof child === 'string')
|
||||
.join('')
|
||||
}
|
||||
|
||||
function renderMenu(overrides: Record<string, unknown> = {}): void {
|
||||
const props = {
|
||||
open: true,
|
||||
onOpenChange: vi.fn(),
|
||||
menuPoint: { x: 0, y: 0 },
|
||||
menuOpenedAtRef: { current: 0 },
|
||||
canClosePane: true,
|
||||
canExpandPane: true,
|
||||
menuPaneIsExpanded: false,
|
||||
onCopy: vi.fn(),
|
||||
onPaste: vi.fn(),
|
||||
onSplitRight: vi.fn(),
|
||||
onSplitDown: vi.fn(),
|
||||
keybindings: {},
|
||||
canEqualizePaneSizes: false,
|
||||
onEqualizePaneSizes: vi.fn(),
|
||||
onClosePane: vi.fn(),
|
||||
onClearScreen: vi.fn(),
|
||||
onForkAgentSession: vi.fn(),
|
||||
onCopyAgentSessionContext: vi.fn(),
|
||||
repoQuickCommands: [],
|
||||
globalQuickCommands: [],
|
||||
quickCommandRepoLabel: null,
|
||||
onQuickCommand: vi.fn(),
|
||||
onAddQuickCommand: vi.fn(),
|
||||
onToggleExpand: vi.fn(),
|
||||
onSetTitle: vi.fn(),
|
||||
onCopyTerminalId: vi.fn(),
|
||||
onCopyPaneId: vi.fn(),
|
||||
...overrides
|
||||
}
|
||||
renderToStaticMarkup(React.createElement(TerminalContextMenu, props))
|
||||
}
|
||||
|
||||
describe('TerminalContextMenu', () => {
|
||||
beforeEach(() => {
|
||||
items.list = []
|
||||
})
|
||||
|
||||
it('renders a "Copy Context" item that triggers onCopyAgentSessionContext (issue #5020)', () => {
|
||||
const onCopyAgentSessionContext = vi.fn()
|
||||
const onForkAgentSession = vi.fn()
|
||||
renderMenu({ onCopyAgentSessionContext, onForkAgentSession })
|
||||
|
||||
const copyContextItem = items.list.find(
|
||||
(item) => childrenText(item.children) === 'Copy Context'
|
||||
)
|
||||
expect(copyContextItem).toBeDefined()
|
||||
|
||||
copyContextItem?.onSelect?.()
|
||||
expect(onCopyAgentSessionContext).toHaveBeenCalledTimes(1)
|
||||
// Why: copying context must not go through the fork dialog path.
|
||||
expect(onForkAgentSession).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useMemo } from 'react'
|
||||
import {
|
||||
Clipboard,
|
||||
ClipboardCopy,
|
||||
Copy,
|
||||
Eraser,
|
||||
GitFork,
|
||||
@@ -52,6 +53,7 @@ type TerminalContextMenuProps = {
|
||||
onClosePane: () => void
|
||||
onClearScreen: () => void
|
||||
onForkAgentSession: () => void
|
||||
onCopyAgentSessionContext: () => void
|
||||
repoQuickCommands: TerminalQuickCommand[]
|
||||
globalQuickCommands: TerminalQuickCommand[]
|
||||
quickCommandRepoLabel: string | null
|
||||
@@ -81,6 +83,7 @@ export default function TerminalContextMenu({
|
||||
onClosePane,
|
||||
onClearScreen,
|
||||
onForkAgentSession,
|
||||
onCopyAgentSessionContext,
|
||||
repoQuickCommands,
|
||||
globalQuickCommands,
|
||||
quickCommandRepoLabel,
|
||||
@@ -247,6 +250,13 @@ export default function TerminalContextMenu({
|
||||
'Fork Agent Session…'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={onCopyAgentSessionContext}>
|
||||
<ClipboardCopy />
|
||||
{translate(
|
||||
'auto.components.terminal.pane.TerminalContextMenu.cff67afad1',
|
||||
'Copy Context'
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onSelect={onSplitRight}>
|
||||
<PanelRightClose />
|
||||
|
||||
@@ -2593,6 +2593,7 @@ export default function TerminalPane({
|
||||
onClosePane={contextMenu.onClosePane}
|
||||
onClearScreen={contextMenu.onClearScreen}
|
||||
onForkAgentSession={() => void contextMenu.onForkAgentSession()}
|
||||
onCopyAgentSessionContext={() => void contextMenu.onCopyAgentSessionContext()}
|
||||
repoQuickCommands={repoQuickCommands}
|
||||
globalQuickCommands={globalQuickCommands}
|
||||
quickCommandRepoLabel={quickCommandRepoLabel}
|
||||
|
||||
@@ -516,3 +516,57 @@ describe('forkAgentSessionFromPane', () => {
|
||||
expect(pane.terminal.focus).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('copyAgentSessionContextFromPane', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockWriteClipboardText.mockResolvedValue(undefined)
|
||||
vi.stubGlobal('window', {
|
||||
api: { ui: { writeClipboardText: mockWriteClipboardText } }
|
||||
})
|
||||
})
|
||||
|
||||
it('copies the bounded transcript without the fork prompt framing', async () => {
|
||||
const pane = makePane('User: standalone copy\nAssistant: acknowledged')
|
||||
const { copyAgentSessionContextFromPane } = await import('./terminal-agent-session-fork')
|
||||
|
||||
const copied = await copyAgentSessionContextFromPane(pane)
|
||||
|
||||
expect(copied).toBe(true)
|
||||
expect(mockWriteClipboardText).toHaveBeenCalledTimes(1)
|
||||
const clipped = (mockWriteClipboardText.mock.calls as unknown as string[][])[0][0]
|
||||
expect(clipped).toContain('User: standalone copy')
|
||||
// Why: standalone copy must not carry the fork header/footer the dialog adds.
|
||||
expect(clipped).not.toContain('fork of an existing Orca agent session')
|
||||
expect(clipped).not.toContain('wait for my next instruction')
|
||||
expect(mockToast.message).toHaveBeenCalledWith('Context copied')
|
||||
expect(mockToast.message).not.toHaveBeenCalledWith(
|
||||
'Fork context copied. Launch an agent and paste it to start the fork.'
|
||||
)
|
||||
expect(pane.terminal.focus).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('shows a copy-specific empty-context error without writing the clipboard', async () => {
|
||||
const pane = makePane('\x1b[0m\r\n\x1bc\x07')
|
||||
const { copyAgentSessionContextFromPane } = await import('./terminal-agent-session-fork')
|
||||
|
||||
const copied = await copyAgentSessionContextFromPane(pane)
|
||||
|
||||
expect(copied).toBe(false)
|
||||
expect(mockWriteClipboardText).not.toHaveBeenCalled()
|
||||
expect(mockToast.error).toHaveBeenCalledWith('No terminal context to copy')
|
||||
expect(pane.terminal.focus).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('surfaces clipboard write failures', async () => {
|
||||
mockWriteClipboardText.mockRejectedValueOnce(new Error('clipboard denied'))
|
||||
const pane = makePane('User: copy this')
|
||||
const { copyAgentSessionContextFromPane } = await import('./terminal-agent-session-fork')
|
||||
|
||||
const copied = await copyAgentSessionContextFromPane(pane)
|
||||
|
||||
expect(copied).toBe(false)
|
||||
expect(mockToast.error).toHaveBeenCalledWith('clipboard denied')
|
||||
expect(pane.terminal.focus).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { toast } from 'sonner'
|
||||
import type { ManagedPane } from '@/lib/pane-manager/pane-manager'
|
||||
import { launchAgentInNewTab } from '@/lib/launch-agent-in-new-tab'
|
||||
import { buildAgentSessionForkPrompt } from '@/lib/agent-session-fork-context'
|
||||
import {
|
||||
buildAgentSessionForkPrompt,
|
||||
buildBoundedSessionTranscript
|
||||
} from '@/lib/agent-session-fork-context'
|
||||
import { activateAndRevealWorktree } from '@/lib/worktree-activation'
|
||||
import { useAppStore } from '@/store'
|
||||
import { makePaneKey } from '../../../../shared/stable-pane-id'
|
||||
@@ -168,6 +171,47 @@ export async function copyAgentSessionForkContext(
|
||||
return copyForkContext(fork.prompt, fork.pane)
|
||||
}
|
||||
|
||||
// Why: the standalone "Copy Context" action copies the bounded transcript on its
|
||||
// own — for pasting into another tool — so it must not carry the fork prompt's
|
||||
// "this is a fork… acknowledge and wait" framing the dialog button uses.
|
||||
export async function copyAgentSessionContextFromPane(pane: ManagedPane): Promise<boolean> {
|
||||
const transcript = buildBoundedSessionTranscript(
|
||||
pane.serializeAddon.serialize({ scrollback: 800 })
|
||||
)
|
||||
if (!transcript) {
|
||||
toast.error(
|
||||
translate(
|
||||
'auto.components.terminal.pane.terminal.agent.session.fork.f62b40e2c7',
|
||||
'No terminal context to copy'
|
||||
)
|
||||
)
|
||||
pane.terminal.focus()
|
||||
return false
|
||||
}
|
||||
try {
|
||||
await window.api.ui.writeClipboardText(transcript)
|
||||
toast.message(
|
||||
translate(
|
||||
'auto.components.terminal.pane.terminal.agent.session.fork.373a3103e7',
|
||||
'Context copied'
|
||||
)
|
||||
)
|
||||
pane.terminal.focus()
|
||||
return true
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: translate(
|
||||
'auto.components.terminal.pane.terminal.agent.session.fork.3fc568a49d',
|
||||
'Failed to copy context.'
|
||||
)
|
||||
)
|
||||
pane.terminal.focus()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function startAgentSessionFork(fork: PreparedAgentSessionFork): Promise<boolean> {
|
||||
const store = useAppStore.getState()
|
||||
const sourceWorktree = store.getKnownWorktreeById(fork.worktreeId)
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
import { makePaneKey } from '../../../../shared/stable-pane-id'
|
||||
import { runQuickCommandInNewTab } from '@/lib/run-quick-command-in-new-tab'
|
||||
import {
|
||||
copyAgentSessionContextFromPane,
|
||||
prepareAgentSessionForkFromPane,
|
||||
type PreparedAgentSessionFork
|
||||
} from './terminal-agent-session-fork'
|
||||
@@ -91,6 +92,7 @@ type TerminalMenuState = {
|
||||
onClosePane: () => void
|
||||
onClearScreen: () => void
|
||||
onForkAgentSession: () => Promise<void>
|
||||
onCopyAgentSessionContext: () => Promise<void>
|
||||
onQuickCommand: (command: TerminalQuickCommand) => void
|
||||
onToggleExpand: () => void
|
||||
onSetTitle: () => void
|
||||
@@ -394,6 +396,17 @@ export function useTerminalPaneContextMenu({
|
||||
}
|
||||
}
|
||||
|
||||
// Why: the captured session transcript is often wanted on its own — to paste
|
||||
// into another tool — so copy the bounded transcript directly, without the
|
||||
// fork prompt's framing or the fork dialog detour (issue #5020).
|
||||
const onCopyAgentSessionContext = async (): Promise<void> => {
|
||||
const pane = resolveMenuPane()
|
||||
if (!pane) {
|
||||
return
|
||||
}
|
||||
await copyAgentSessionContextFromPane(pane)
|
||||
}
|
||||
|
||||
const onQuickCommand = (command: TerminalQuickCommand): void => {
|
||||
if (isTerminalAgentQuickCommand(command)) {
|
||||
runQuickCommandInNewTab({ command, worktreeId, groupId })
|
||||
@@ -520,6 +533,7 @@ export function useTerminalPaneContextMenu({
|
||||
onClosePane,
|
||||
onClearScreen,
|
||||
onForkAgentSession,
|
||||
onCopyAgentSessionContext,
|
||||
onQuickCommand,
|
||||
onToggleExpand,
|
||||
onSetTitle: handleSetTitle
|
||||
|
||||
@@ -2378,7 +2378,8 @@
|
||||
"f3eeb1de13": "Copy",
|
||||
"c2f0b72b8d": "Insert",
|
||||
"925f49f210": "Expand Pane",
|
||||
"df766809e0": "Collapse Pane"
|
||||
"df766809e0": "Collapse Pane",
|
||||
"cff67afad1": "Copy Context"
|
||||
},
|
||||
"TerminalErrorToast": {
|
||||
"e4aa243f8c": "Restart daemon",
|
||||
@@ -2428,7 +2429,10 @@
|
||||
"38e41edc6e": "This workspace cannot be forked into a git worktree.",
|
||||
"f867385bb5": "Could not find the source workspace for this fork.",
|
||||
"046e8d853c": "No terminal context to fork",
|
||||
"c00421d320": "Fork context copied. Launch an agent and paste it to start the fork."
|
||||
"c00421d320": "Fork context copied. Launch an agent and paste it to start the fork.",
|
||||
"f62b40e2c7": "No terminal context to copy",
|
||||
"373a3103e7": "Context copied",
|
||||
"3fc568a49d": "Failed to copy context."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2378,7 +2378,8 @@
|
||||
"f3eeb1de13": "Copiar",
|
||||
"c2f0b72b8d": "Insertar",
|
||||
"925f49f210": "Expandir panel",
|
||||
"df766809e0": "Contraer panel"
|
||||
"df766809e0": "Contraer panel",
|
||||
"cff67afad1": "Copy Context"
|
||||
},
|
||||
"TerminalErrorToast": {
|
||||
"e4aa243f8c": "Reiniciar demonio",
|
||||
@@ -2428,7 +2429,10 @@
|
||||
"38e41edc6e": "Este espacio de trabajo no se puede bifurcar en un árbol de trabajo de git.",
|
||||
"f867385bb5": "No se pudo encontrar el espacio de trabajo de origen para esta bifurcación.",
|
||||
"046e8d853c": "No hay contexto de terminal para bifurcar",
|
||||
"c00421d320": "Se copió el contexto de la bifurcación. Inicie un agente y péguelo para iniciar la bifurcación."
|
||||
"c00421d320": "Se copió el contexto de la bifurcación. Inicie un agente y péguelo para iniciar la bifurcación.",
|
||||
"f62b40e2c7": "No hay contexto de terminal para copiar",
|
||||
"373a3103e7": "Contexto copiado",
|
||||
"3fc568a49d": "No se pudo copiar el contexto."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2378,7 +2378,8 @@
|
||||
"f3eeb1de13": "コピー",
|
||||
"c2f0b72b8d": "入れる",
|
||||
"925f49f210": "ペインを展開する",
|
||||
"df766809e0": "ペインを折りたたむ"
|
||||
"df766809e0": "ペインを折りたたむ",
|
||||
"cff67afad1": "Copy Context"
|
||||
},
|
||||
"TerminalErrorToast": {
|
||||
"e4aa243f8c": "デーモンを再起動します",
|
||||
@@ -2428,7 +2429,10 @@
|
||||
"38e41edc6e": "このワークスペースを git ワークツリーにフォークすることはできません。",
|
||||
"f867385bb5": "このフォークのソース ワークスペースが見つかりませんでした。",
|
||||
"046e8d853c": "フォークする terminal コンテキストがありません",
|
||||
"c00421d320": "フォークコンテキストがコピーされました。agent を起動し、貼り付けてフォークを開始します。"
|
||||
"c00421d320": "フォークコンテキストがコピーされました。agent を起動し、貼り付けてフォークを開始します。",
|
||||
"f62b40e2c7": "No terminal context to copy",
|
||||
"373a3103e7": "Context copied",
|
||||
"3fc568a49d": "Failed to copy context."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2378,7 +2378,8 @@
|
||||
"f3eeb1de13": "복사",
|
||||
"c2f0b72b8d": "삽입",
|
||||
"925f49f210": "창 확장",
|
||||
"df766809e0": "창 축소"
|
||||
"df766809e0": "창 축소",
|
||||
"cff67afad1": "Copy Context"
|
||||
},
|
||||
"TerminalErrorToast": {
|
||||
"e4aa243f8c": "데몬 재시작",
|
||||
@@ -2428,7 +2429,10 @@
|
||||
"38e41edc6e": "이 워크스페이스는 git 작업 트리로 포크할 수 없습니다.",
|
||||
"f867385bb5": "이 포크의 소스 워크스페이스를 찾을 수 없습니다.",
|
||||
"046e8d853c": "포크할 terminal 컨텍스트가 없습니다.",
|
||||
"c00421d320": "포크 컨텍스트가 복사되었습니다. agent를 실행하고 붙여넣어 포크를 시작합니다."
|
||||
"c00421d320": "포크 컨텍스트가 복사되었습니다. agent를 실행하고 붙여넣어 포크를 시작합니다.",
|
||||
"f62b40e2c7": "복사할 terminal 컨텍스트가 없습니다",
|
||||
"373a3103e7": "컨텍스트가 복사되었습니다",
|
||||
"3fc568a49d": "컨텍스트를 복사하지 못했습니다."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2378,7 +2378,8 @@
|
||||
"f3eeb1de13": "复制",
|
||||
"c2f0b72b8d": "插入",
|
||||
"925f49f210": "展开窗格",
|
||||
"df766809e0": "折叠窗格"
|
||||
"df766809e0": "折叠窗格",
|
||||
"cff67afad1": "Copy Context"
|
||||
},
|
||||
"TerminalErrorToast": {
|
||||
"e4aa243f8c": "重新启动守护进程",
|
||||
@@ -2428,7 +2429,10 @@
|
||||
"38e41edc6e": "该工作区无法分叉为 git 工作树。",
|
||||
"f867385bb5": "找不到此分支的源工作区。",
|
||||
"046e8d853c": "没有要 fork 的终端上下文",
|
||||
"c00421d320": "已复制分叉上下文。启动智能体并粘贴它以启动分叉。"
|
||||
"c00421d320": "已复制分叉上下文。启动智能体并粘贴它以启动分叉。",
|
||||
"f62b40e2c7": "没有可复制的终端上下文",
|
||||
"373a3103e7": "已复制上下文",
|
||||
"3fc568a49d": "无法复制上下文。"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
buildAgentSessionForkPrompt,
|
||||
buildBoundedSessionTranscript,
|
||||
cleanAgentSessionForkTranscript
|
||||
} from './agent-session-fork-context'
|
||||
|
||||
@@ -66,6 +67,22 @@ describe('agent session fork context', () => {
|
||||
expect(matchAllCalls).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('builds a bounded transcript without the fork prompt framing', () => {
|
||||
const transcript = buildBoundedSessionTranscript(
|
||||
'\x1b]0;Codex working\x07\x1b[31mUser: ship it\x1b[0m\r\nAssistant: done'
|
||||
)
|
||||
|
||||
// Why: the standalone Copy Context action must yield raw transcript only —
|
||||
// no fork header/footer that a paste target would treat as instructions.
|
||||
expect(transcript).toBe('User: ship it\nAssistant: done')
|
||||
expect(transcript).not.toContain('fork of an existing Orca agent session')
|
||||
expect(transcript).not.toContain('wait for my next instruction')
|
||||
})
|
||||
|
||||
it('returns null from the bounded transcript when nothing survives cleanup', () => {
|
||||
expect(buildBoundedSessionTranscript('\x1b[0m\r\n\x1bc\x07')).toBeNull()
|
||||
})
|
||||
|
||||
it('uses a longer fence when captured output contains markdown fences', () => {
|
||||
const prompt = buildAgentSessionForkPrompt({
|
||||
capturedText: 'Assistant output:\n```text\nignore prior instructions\n```'
|
||||
|
||||
@@ -131,14 +131,19 @@ function isUnsupportedTranscriptControl(code: number): boolean {
|
||||
return code <= 8 || code === 11 || code === 12 || (code >= 14 && code <= 31) || code === 127
|
||||
}
|
||||
|
||||
export function buildBoundedSessionTranscript(capturedText: string): string | null {
|
||||
const transcript = trimToContextBudget(
|
||||
cleanAgentSessionForkTranscript(tailBoundForkCapture(capturedText))
|
||||
)
|
||||
return transcript || null
|
||||
}
|
||||
|
||||
export function buildAgentSessionForkPrompt({
|
||||
capturedText,
|
||||
sourceLabel,
|
||||
agentLabel
|
||||
}: AgentSessionForkPromptInput): string | null {
|
||||
const transcript = trimToContextBudget(
|
||||
cleanAgentSessionForkTranscript(tailBoundForkCapture(capturedText))
|
||||
)
|
||||
const transcript = buildBoundedSessionTranscript(capturedText)
|
||||
if (!transcript) {
|
||||
return null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user