feat(secretary): захват выдачи инструмента (N3) + сверка имени дела при включении (N2)
- parseLastExchange привязывает результат инструмента к действию по tool_use_id, склеивает text-блоки, усекает до 1200 симв.; [ВЫДАЧА] в Слое 1 теперь наполняется - resolveCaseActivation: похожее имя дела (опечатка/подстрока) -> переспросить, не заводя дело-двойник; хук secretary-prompt-hook выводит подсказку с кандидатами - TDD: тесты secretary-transcript/flag/prompt-hook; полный свод зелёный Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,24 @@ function isRealUserPrompt(msg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Последний обмен из стенограммы: { user, assistant, actions:[{tool,input}] }. */
|
||||
// Текст результата инструмента: строка как есть; массив блоков → склейка text-блоков.
|
||||
const MAX_RESULT_CHARS = 1200;
|
||||
function resultText(content) {
|
||||
if (typeof content === 'string') return content;
|
||||
if (Array.isArray(content)) {
|
||||
return content.filter((b) => b && b.type === 'text' && typeof b.text === 'string')
|
||||
.map((b) => b.text).join('\n');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function truncateResult(s) {
|
||||
const t = String(s ?? '');
|
||||
return t.length > MAX_RESULT_CHARS ? t.slice(0, MAX_RESULT_CHARS) + '…' : t;
|
||||
}
|
||||
|
||||
/** Последний обмен из стенограммы: { user, assistant, actions:[{tool,input,result?}] }.
|
||||
* result привязывается к действию по tool_use.id === tool_result.tool_use_id (усечён до предела);
|
||||
* без совпадения действие остаётся прежней формы {tool,input} — без ключа result. */
|
||||
export function parseLastExchange(transcriptText) {
|
||||
const entries = parseLines(transcriptText);
|
||||
let u = -1;
|
||||
@@ -38,19 +55,31 @@ export function parseLastExchange(transcriptText) {
|
||||
: '');
|
||||
|
||||
let assistant = '';
|
||||
const actions = [];
|
||||
const raw = []; // {id, tool, input} — вызовы инструментов
|
||||
const results = {}; // tool_use_id -> текст результата (из tool_result в сообщениях role:user)
|
||||
for (let i = u + 1; i < entries.length; i++) {
|
||||
const m = entries[i] && entries[i].message;
|
||||
if (!m || m.role !== 'assistant') continue;
|
||||
if (!m) continue;
|
||||
const c = m.content;
|
||||
if (Array.isArray(c)) {
|
||||
for (const b of c) {
|
||||
if (b && b.type === 'text' && b.text) assistant += (assistant ? '\n' : '') + b.text;
|
||||
if (b && b.type === 'tool_use') actions.push({ tool: b.name, input: JSON.stringify(b.input ?? {}) });
|
||||
if (m.role === 'assistant') {
|
||||
if (Array.isArray(c)) {
|
||||
for (const b of c) {
|
||||
if (b && b.type === 'text' && b.text) assistant += (assistant ? '\n' : '') + b.text;
|
||||
if (b && b.type === 'tool_use') raw.push({ id: b.id, tool: b.name, input: JSON.stringify(b.input ?? {}) });
|
||||
}
|
||||
} else if (typeof c === 'string') {
|
||||
assistant += (assistant ? '\n' : '') + c;
|
||||
}
|
||||
} else if (m.role === 'user' && Array.isArray(c)) {
|
||||
for (const b of c) {
|
||||
if (b && b.type === 'tool_result' && b.tool_use_id != null) results[b.tool_use_id] = resultText(b.content);
|
||||
}
|
||||
} else if (typeof c === 'string') {
|
||||
assistant += (assistant ? '\n' : '') + c;
|
||||
}
|
||||
}
|
||||
const actions = raw.map((a) => {
|
||||
const out = { tool: a.tool, input: a.input };
|
||||
if (a.id != null && results[a.id] != null) out.result = truncateResult(results[a.id]);
|
||||
return out;
|
||||
});
|
||||
return { user, assistant, actions };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user