mister-wolf 2.14.0 → 2.15.0
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/README.md +2 -0
- package/README.ru.md +2 -0
- package/dist/adapters/cli/commands/memory-doctor.d.ts.map +1 -1
- package/dist/adapters/cli/commands/memory-doctor.js +32 -1
- package/dist/adapters/cli/commands/memory-doctor.js.map +1 -1
- package/dist/adapters/cli/commands/memory-scaffold.d.ts.map +1 -1
- package/dist/adapters/cli/commands/memory-scaffold.js +15 -0
- package/dist/adapters/cli/commands/memory-scaffold.js.map +1 -1
- package/dist/adapters/cli/commands/memory-sync.d.ts.map +1 -1
- package/dist/adapters/cli/commands/memory-sync.js +8 -0
- package/dist/adapters/cli/commands/memory-sync.js.map +1 -1
- package/dist/adapters/fs/artifact-fs.d.ts +4 -0
- package/dist/adapters/fs/artifact-fs.d.ts.map +1 -0
- package/dist/adapters/fs/artifact-fs.js +29 -0
- package/dist/adapters/fs/artifact-fs.js.map +1 -0
- package/dist/adapters/platforms/opencode-adapter.d.ts +8 -1
- package/dist/adapters/platforms/opencode-adapter.d.ts.map +1 -1
- package/dist/adapters/platforms/opencode-adapter.js +33 -0
- package/dist/adapters/platforms/opencode-adapter.js.map +1 -1
- package/dist/app/use-cases/artifact-front-matter.d.ts +5 -0
- package/dist/app/use-cases/artifact-front-matter.d.ts.map +1 -0
- package/dist/app/use-cases/artifact-front-matter.js +16 -0
- package/dist/app/use-cases/artifact-front-matter.js.map +1 -0
- package/dist/app/use-cases/init-project.d.ts +3 -1
- package/dist/app/use-cases/init-project.d.ts.map +1 -1
- package/dist/app/use-cases/init-project.js +36 -16
- package/dist/app/use-cases/init-project.js.map +1 -1
- package/dist/app/use-cases/lint-artifacts.d.ts +18 -0
- package/dist/app/use-cases/lint-artifacts.d.ts.map +1 -0
- package/dist/app/use-cases/lint-artifacts.js +249 -0
- package/dist/app/use-cases/lint-artifacts.js.map +1 -0
- package/dist/app/use-cases/scaffold-agent.d.ts +1 -1
- package/dist/app/use-cases/scaffold-agent.d.ts.map +1 -1
- package/dist/app/use-cases/scaffold-agent.js +1 -1
- package/dist/app/use-cases/scaffold-agent.js.map +1 -1
- package/dist/app/use-cases/scaffold-artifact.d.ts +22 -0
- package/dist/app/use-cases/scaffold-artifact.d.ts.map +1 -0
- package/dist/app/use-cases/scaffold-artifact.js +153 -0
- package/dist/app/use-cases/scaffold-artifact.js.map +1 -0
- package/dist/app/use-cases/sync-artifact-index.d.ts +20 -0
- package/dist/app/use-cases/sync-artifact-index.d.ts.map +1 -0
- package/dist/app/use-cases/sync-artifact-index.js +68 -0
- package/dist/app/use-cases/sync-artifact-index.js.map +1 -0
- package/dist/domain/memory-types.d.ts +12 -0
- package/dist/domain/memory-types.d.ts.map +1 -1
- package/dist/domain/memory-types.js +4 -0
- package/dist/domain/memory-types.js.map +1 -1
- package/dist/domain/schemas/relation-schema.d.ts +1 -1
- package/dist/ports/platform-adapter.port.d.ts +15 -0
- package/dist/ports/platform-adapter.port.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/.wolf/router.log +1 -0
- package/templates/base/skills/finishing-a-development-branch/SKILL.md +32 -13
- package/templates/base/skills/receiving-code-review/SKILL.md +39 -0
- package/templates/base/skills/test-driven-development/SKILL.md +59 -37
- package/templates/base/skills/using-git-worktrees/SKILL.md +25 -15
- package/templates/base/skills/verification-before-completion/SKILL.md +39 -25
- package/templates/base/skills/wolf-brainstorm/SKILL.md +51 -36
- package/templates/base/skills/wolf-design/SKILL.md +45 -0
- package/templates/base/skills/wolf-execute/SKILL.md +30 -13
- package/templates/base/skills/wolf-plan/SKILL.md +38 -21
- package/templates/base/skills/wolf-review/SKILL.md +33 -14
- package/templates/base/skills/wolf-sdd/SKILL.md +38 -16
- package/templates/base/skills/wolf-skill-intake/SKILL.md +66 -0
- package/templates/base/skills/wolf-testplan/SKILL.md +37 -0
- package/templates/base/skills/writing-skills/SKILL.md +40 -0
- package/templates/opencode/plugins/wolf-router.ts +110 -52
- package/templates/opencode/plugins/wolf-session-start.js +88 -40
|
@@ -115,61 +115,119 @@ async function resolvePlaybook(agentId: string): Promise<{ id: string; body: str
|
|
|
115
115
|
return found;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
// B3: метрика вызовов скиллов. Форма входа v1: input.tool — имя тула,
|
|
119
|
+
// output.args — объект аргументов; v2: event.tool + event.input (name | skill —
|
|
120
|
+
// фолбэк). Отдельный JSONL-файл — инвариант: enum событий SignalEventSchema
|
|
121
|
+
// закрытый, в общий router.log эти события класть нельзя. WOLF_SKILL_LOG —
|
|
122
|
+
// тест-шов (читается на каждый вызов: env ставится тестами уже после импорта).
|
|
123
|
+
function recordSkillMetric(tool: unknown, args: unknown): void {
|
|
124
|
+
try {
|
|
125
|
+
if (tool !== 'skill') return;
|
|
126
|
+
const a = (args ?? {}) as { name?: string; skill?: string } | null;
|
|
127
|
+
const skill = a?.name ?? a?.skill;
|
|
128
|
+
if (!skill) return;
|
|
129
|
+
const file =
|
|
130
|
+
process.env.WOLF_SKILL_LOG ?? path.join(PROJECT_ROOT, '.wolf', 'metrics', 'skill-invocations.jsonl');
|
|
131
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
132
|
+
const entry: { ts: string; skill: string; agent?: string } = { ts: new Date().toISOString(), skill };
|
|
133
|
+
if (lastAgentId) entry.agent = lastAgentId;
|
|
134
|
+
fs.appendFileSync(file, `${JSON.stringify(entry)}\n`);
|
|
135
|
+
} catch {
|
|
136
|
+
/* fail-safe: падение метрики не роняет сессию */
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Общая доставка playbook (v1 system.transform / v2 context): joined system-текст
|
|
141
|
+
// → маркер agent-id → гвард идемпотентности INJECT_HEADER → resolvePlaybook →
|
|
142
|
+
// push(текст). Fail-safe: не роняем сессию ни на одном шаге.
|
|
143
|
+
async function deliverPlaybook(joined: string, push: (text: string) => void): Promise<void> {
|
|
144
|
+
try {
|
|
145
|
+
const m = joined.match(AGENT_ID_RE);
|
|
146
|
+
if (!m) return; // рамка без маркера — не наша забота
|
|
147
|
+
if (joined.includes(INJECT_HEADER)) return; // идемпотентность: не вставляем дважды
|
|
148
|
+
const agentId = m[1];
|
|
149
|
+
lastAgentId = agentId; // агент известен хуку skill-метрики (B3)
|
|
150
|
+
const startedAt = Date.now();
|
|
151
|
+
const resolved = await resolvePlaybook(agentId);
|
|
152
|
+
const ms = Date.now() - startedAt;
|
|
153
|
+
if (resolved) {
|
|
154
|
+
push(`\n\n${INJECT_HEADER}\n\n${resolved.body}`);
|
|
155
|
+
logRoute(
|
|
156
|
+
`agent-id=${agentId} playbook=hit name=${resolved.id} variant=canonical injected=yes` +
|
|
157
|
+
` ms=${ms} bytes=${Buffer.byteLength(resolved.body)}`
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
// T012: miss канона → универсальный fallback; инъекция для любого agent-id
|
|
161
|
+
push(`\n\n${INJECT_HEADER}\n\n${FALLBACK_PLAYBOOK}`);
|
|
162
|
+
logRoute(
|
|
163
|
+
`agent-id=${agentId} playbook=hit name=fallback variant=fallback injected=yes` +
|
|
164
|
+
` ms=${ms} bytes=${Buffer.byteLength(FALLBACK_PLAYBOOK)}`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
/* fail-safe: не роняем сессию */
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 4.C: один WOLF_SESSION на процесс opencode — спавны волка и bash-вызовы
|
|
173
|
+
// наследуют ключ. Известный потолок: несколько чатов одного процесса opencode
|
|
174
|
+
// шарят ключ — консервативная недо-дедупликация, не ошибка.
|
|
175
|
+
function ensureSession(): void {
|
|
122
176
|
if (!process.env.WOLF_SESSION) process.env.WOLF_SESSION = 'opc-' + randomUUID();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// v1-фабрика: loader v1 вызывает `server()` и получает hook-объект.
|
|
180
|
+
async function server() {
|
|
181
|
+
ensureSession();
|
|
123
182
|
return {
|
|
124
|
-
'experimental.chat.system.transform': async (_input, output) => {
|
|
125
|
-
|
|
126
|
-
const joined = output.system.join('\n');
|
|
127
|
-
const m = joined.match(AGENT_ID_RE);
|
|
128
|
-
if (!m) return; // рамка без маркера — не наша забота
|
|
129
|
-
if (joined.includes(INJECT_HEADER)) return; // идемпотентность: не вставляем дважды
|
|
130
|
-
const agentId = m[1];
|
|
131
|
-
lastAgentId = agentId; // агент известен хуку skill-метрики (B3)
|
|
132
|
-
const startedAt = Date.now();
|
|
133
|
-
const resolved = await resolvePlaybook(agentId);
|
|
134
|
-
const ms = Date.now() - startedAt;
|
|
135
|
-
if (resolved) {
|
|
136
|
-
output.system.push(`\n\n${INJECT_HEADER}\n\n${resolved.body}`);
|
|
137
|
-
logRoute(
|
|
138
|
-
`agent-id=${agentId} playbook=hit name=${resolved.id} variant=canonical injected=yes` +
|
|
139
|
-
` ms=${ms} bytes=${Buffer.byteLength(resolved.body)}`
|
|
140
|
-
);
|
|
141
|
-
} else {
|
|
142
|
-
// T012: miss канона → универсальный fallback; инъекция для любого agent-id
|
|
143
|
-
output.system.push(`\n\n${INJECT_HEADER}\n\n${FALLBACK_PLAYBOOK}`);
|
|
144
|
-
logRoute(
|
|
145
|
-
`agent-id=${agentId} playbook=hit name=fallback variant=fallback injected=yes` +
|
|
146
|
-
` ms=${ms} bytes=${Buffer.byteLength(FALLBACK_PLAYBOOK)}`
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
} catch {
|
|
150
|
-
/* fail-safe: не роняем сессию */
|
|
151
|
-
}
|
|
183
|
+
'experimental.chat.system.transform': async (_input: unknown, output: { system: string[] }) => {
|
|
184
|
+
await deliverPlaybook((output?.system ?? []).join('\n'), (text) => output.system.push(text));
|
|
152
185
|
},
|
|
153
|
-
// B3: метрика вызовов скиллов. Форма входа по докам opencode: input.tool —
|
|
154
|
-
// имя тула, output.args — объект аргументов (name | skill — фолбэк).
|
|
155
|
-
// Отдельный JSONL-файл — инвариант: enum событий SignalEventSchema закрытый,
|
|
156
|
-
// в общий router.log эти события класть нельзя. WOLF_SKILL_LOG — тест-шов
|
|
157
|
-
// (читается на каждый вызов: env ставится тестами уже после импорта модуля).
|
|
158
186
|
'tool.execute.before': async (input: unknown, output: unknown) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (!skill) return;
|
|
164
|
-
const file =
|
|
165
|
-
process.env.WOLF_SKILL_LOG ?? path.join(PROJECT_ROOT, '.wolf', 'metrics', 'skill-invocations.jsonl');
|
|
166
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
167
|
-
const entry: { ts: string; skill: string; agent?: string } = { ts: new Date().toISOString(), skill };
|
|
168
|
-
if (lastAgentId) entry.agent = lastAgentId;
|
|
169
|
-
fs.appendFileSync(file, `${JSON.stringify(entry)}\n`);
|
|
170
|
-
} catch {
|
|
171
|
-
/* fail-safe: падение метрики не роняет сессию */
|
|
172
|
-
}
|
|
187
|
+
recordSkillMetric(
|
|
188
|
+
(input as { tool?: string } | null)?.tool,
|
|
189
|
+
(output as { args?: { name?: string; skill?: string } } | null)?.args
|
|
190
|
+
);
|
|
173
191
|
},
|
|
174
192
|
};
|
|
175
|
-
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// v2-контракт: loader v2 вызывает `setup(ctx)`; инъекция — текст-блок в
|
|
196
|
+
// event.system (мутация доходит до модельного запроса), метрика — tool-hook.
|
|
197
|
+
interface V2ContextEvent {
|
|
198
|
+
system: Array<{ type: 'text'; text: string }>;
|
|
199
|
+
messages: Array<{ id: string; role: string; content: Array<{ type: 'text'; text: string }>; metadata: unknown }>;
|
|
200
|
+
agent: string;
|
|
201
|
+
}
|
|
202
|
+
interface V2ToolEvent {
|
|
203
|
+
tool: string;
|
|
204
|
+
input: unknown;
|
|
205
|
+
id: string;
|
|
206
|
+
}
|
|
207
|
+
interface V2Ctx {
|
|
208
|
+
session: {
|
|
209
|
+
hook(name: 'context', fn: (event: V2ContextEvent) => Promise<void>): Promise<{ dispose: () => Promise<void> }>;
|
|
210
|
+
};
|
|
211
|
+
tool: {
|
|
212
|
+
hook(
|
|
213
|
+
name: 'execute.before',
|
|
214
|
+
fn: (event: V2ToolEvent) => Promise<void>
|
|
215
|
+
): Promise<{ dispose: () => Promise<void> }>;
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function setup(ctx: V2Ctx) {
|
|
220
|
+
ensureSession();
|
|
221
|
+
await ctx.session.hook('context', async (event) => {
|
|
222
|
+
await deliverPlaybook(
|
|
223
|
+
(event?.system ?? []).map((s) => s.text).join('\n'),
|
|
224
|
+
(text) => event.system.push({ type: 'text', text })
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
await ctx.tool.hook('execute.before', async (event) => {
|
|
228
|
+
recordSkillMetric(event.tool, event.input);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// РОВНО ОДИН export на файл (контракт loader'а opencode): dual v1/v2 entry.
|
|
233
|
+
export default { id: 'wolf-router', setup, server };
|
|
@@ -56,16 +56,15 @@ const WORKER_BODY = `## using-skills: пассивный режим воркер
|
|
|
56
56
|
/**
|
|
57
57
|
* Чистая функция инъекции (юнит-тестируемая): по маркеру в транскрипте и
|
|
58
58
|
* agent-id решает, какое тело доставить. null = инъекция не нужна.
|
|
59
|
-
*
|
|
59
|
+
* Shape-agnostic (v1 parts / v2 content): части сообщения — `m.parts` ИЛИ
|
|
60
|
+
* `m.content`. Fail-safe (m13): битый вход → null, никогда не бросает.
|
|
60
61
|
*/
|
|
61
62
|
function computeInjection(messages, agentId) {
|
|
62
63
|
try {
|
|
63
64
|
if (!Array.isArray(messages)) return null;
|
|
64
65
|
// H3: идемпотентность — маркер в ЛЮБОМ сообщении транскрипта → не инъекцируем.
|
|
65
|
-
const seen = messages.some(
|
|
66
|
-
(m) =>
|
|
67
|
-
Array.isArray(m?.parts) &&
|
|
68
|
-
m.parts.some((p) => p?.type === 'text' && String(p.text ?? '').includes(MARKER))
|
|
66
|
+
const seen = messages.some((m) =>
|
|
67
|
+
blockList(m).some((p) => p?.type === 'text' && String(p.text ?? '').includes(MARKER))
|
|
69
68
|
);
|
|
70
69
|
if (seen) return null;
|
|
71
70
|
// MAJ-4: worker-* → усечённое тело (без диспетчерского контура using-skills).
|
|
@@ -95,9 +94,13 @@ const runWolf = (args) => {
|
|
|
95
94
|
.catch(() => null);
|
|
96
95
|
};
|
|
97
96
|
|
|
97
|
+
// Блоки контента сообщения: v1 транскрипт — m.parts, v2 context — m.content.
|
|
98
|
+
const blockList = (message) =>
|
|
99
|
+
Array.isArray(message?.parts) ? message.parts : Array.isArray(message?.content) ? message.content : [];
|
|
100
|
+
|
|
98
101
|
// Topic = первые слова первого пользовательского сообщения, для `call --for`.
|
|
99
102
|
const topicOf = (message) =>
|
|
100
|
-
(message
|
|
103
|
+
blockList(message)
|
|
101
104
|
.filter((p) => p.type === 'text' && p.text)
|
|
102
105
|
.map((p) => p.text)
|
|
103
106
|
.join(' ')
|
|
@@ -106,49 +109,94 @@ const topicOf = (message) =>
|
|
|
106
109
|
.slice(0, 10)
|
|
107
110
|
.join(' ');
|
|
108
111
|
|
|
112
|
+
/** MAJ-1: БЕЗ кэша на процесс — каждый запуск без маркера пересчитывает
|
|
113
|
+
* recap/call заново (маркер исчез после /clear или compact → снова).
|
|
114
|
+
* L2 (worker-*) секции recap/call не трогает вообще. */
|
|
115
|
+
async function buildSections(body, firstUser, agentId) {
|
|
116
|
+
const sections = [body];
|
|
117
|
+
if (!(agentId ?? '').startsWith('worker-')) {
|
|
118
|
+
const topic = topicOf(firstUser);
|
|
119
|
+
const [recap, call] = await Promise.all([
|
|
120
|
+
runWolf(['recap']),
|
|
121
|
+
topic ? runWolf(['call', '--for', topic, '--compact', '600']) : null,
|
|
122
|
+
]);
|
|
123
|
+
if (call?.trim()) sections.unshift(`## Active injections\n${call.trim()}`);
|
|
124
|
+
if (recap?.trim()) sections.unshift(`## Recap\n${recap.trim()}`);
|
|
125
|
+
}
|
|
126
|
+
return sections;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const wrap = (sections) =>
|
|
130
|
+
`<session_context>\n${MARKER}\n\n${sections.join('\n\n')}\n</session_context>`;
|
|
131
|
+
|
|
109
132
|
// MAJ-4: уровень сессии читаем из system-промпта (маркер agent-id в теле
|
|
110
133
|
// рамки агента). Порядок хуков на первый запрос — на совести рантайма;
|
|
111
134
|
// пока agent-id не виден, дефолт — полный набор (L0/L1, primary-сессии).
|
|
112
135
|
let currentAgentId = null;
|
|
113
136
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
137
|
+
// 4.C/P204: один WOLF_SESSION на процесс opencode — и v1-, и v2-фабрика
|
|
138
|
+
// ставят ключ только при отсутствии (спавны наследуют process.env).
|
|
139
|
+
function ensureSession() {
|
|
140
|
+
if (!process.env.WOLF_SESSION) process.env.WOLF_SESSION = 'opc-' + randomUUID();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// v1-фабрика: loader v1 вызывает `server()` и получает hook-объект.
|
|
144
|
+
async function server() {
|
|
145
|
+
ensureSession();
|
|
146
|
+
return {
|
|
147
|
+
'experimental.chat.system.transform': async (_input, output) => {
|
|
148
|
+
try {
|
|
149
|
+
const m = (output?.system ?? []).join('\n').match(AGENT_ID_RE);
|
|
150
|
+
currentAgentId = m ? m[1] : null;
|
|
151
|
+
} catch {
|
|
152
|
+
/* fail-safe */
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
'experimental.chat.messages.transform': async (_input, output) => {
|
|
156
|
+
try {
|
|
157
|
+
const messages = output?.messages;
|
|
158
|
+
const body = computeInjection(messages, currentAgentId);
|
|
159
|
+
if (!body) return;
|
|
160
|
+
const firstUser = messages.find((m) => m.info?.role === 'user');
|
|
161
|
+
if (!firstUser?.parts?.length) return;
|
|
162
|
+
|
|
163
|
+
const sections = await buildSections(body, firstUser, currentAgentId);
|
|
164
|
+
const ref = firstUser.parts[0];
|
|
165
|
+
firstUser.parts.unshift({
|
|
166
|
+
...ref,
|
|
167
|
+
type: 'text',
|
|
168
|
+
text: wrap(sections),
|
|
169
|
+
});
|
|
170
|
+
} catch {
|
|
171
|
+
// Fail-safe (m13): ошибка плагина не имеет права уронить сессию.
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// v2-контракт: loader v2 вызывает `setup(ctx)`. agent-id — из текстов
|
|
178
|
+
// event.system (маркер рамки), первый user — по m.role, инъекция — text-блок
|
|
179
|
+
// в начало content (мутация доходит до модельного запроса).
|
|
180
|
+
async function setup(ctx) {
|
|
181
|
+
ensureSession();
|
|
182
|
+
await ctx.session.hook('context', async (event) => {
|
|
124
183
|
try {
|
|
125
|
-
const messages =
|
|
184
|
+
const messages = event?.messages;
|
|
185
|
+
const systemText = (event?.system ?? []).map((s) => s?.text ?? '').join('\n');
|
|
186
|
+
const m = systemText.match(AGENT_ID_RE);
|
|
187
|
+
currentAgentId = m ? m[1] : typeof event?.agent === 'string' ? event.agent : null;
|
|
126
188
|
const body = computeInjection(messages, currentAgentId);
|
|
127
189
|
if (!body) return;
|
|
128
|
-
const firstUser = messages.find((
|
|
129
|
-
if (!firstUser?.
|
|
130
|
-
|
|
131
|
-
const sections = [body];
|
|
132
|
-
// MAJ-1: БЕЗ кэша на процесс — каждый запуск без маркера пересчитывает
|
|
133
|
-
// recap/call заново (маркер исчез после /clear или compact → снова).
|
|
134
|
-
if (!(currentAgentId ?? '').startsWith('worker-')) {
|
|
135
|
-
const topic = topicOf(firstUser);
|
|
136
|
-
const [recap, call] = await Promise.all([
|
|
137
|
-
runWolf(['recap']),
|
|
138
|
-
topic ? runWolf(['call', '--for', topic, '--compact', '600']) : null,
|
|
139
|
-
]);
|
|
140
|
-
if (call?.trim()) sections.unshift(`## Active injections\n${call.trim()}`);
|
|
141
|
-
if (recap?.trim()) sections.unshift(`## Recap\n${recap.trim()}`);
|
|
142
|
-
}
|
|
190
|
+
const firstUser = Array.isArray(messages) ? messages.find((mm) => mm?.role === 'user') : null;
|
|
191
|
+
if (!Array.isArray(firstUser?.content) || firstUser.content.length === 0) return;
|
|
143
192
|
|
|
144
|
-
const
|
|
145
|
-
firstUser.
|
|
146
|
-
...ref,
|
|
147
|
-
type: 'text',
|
|
148
|
-
text: `<session_context>\n${MARKER}\n\n${sections.join('\n\n')}\n</session_context>`,
|
|
149
|
-
});
|
|
193
|
+
const sections = await buildSections(body, firstUser, currentAgentId);
|
|
194
|
+
firstUser.content.unshift({ type: 'text', text: wrap(sections) });
|
|
150
195
|
} catch {
|
|
151
196
|
// Fail-safe (m13): ошибка плагина не имеет права уронить сессию.
|
|
152
197
|
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// РОВНО ОДИН export на файл (контракт loader'а opencode): dual v1/v2 entry.
|
|
202
|
+
export default { id: 'wolf-session-start', setup, server };
|