@moxxy/mode-deep-research 0.26.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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/dist/approval.d.ts +32 -0
  3. package/dist/approval.d.ts.map +1 -0
  4. package/dist/approval.js +84 -0
  5. package/dist/approval.js.map +1 -0
  6. package/dist/constants.d.ts +62 -0
  7. package/dist/constants.d.ts.map +1 -0
  8. package/dist/constants.js +141 -0
  9. package/dist/constants.js.map +1 -0
  10. package/dist/fanout-phase.d.ts +44 -0
  11. package/dist/fanout-phase.d.ts.map +1 -0
  12. package/dist/fanout-phase.js +133 -0
  13. package/dist/fanout-phase.js.map +1 -0
  14. package/dist/index.d.ts +7 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +18 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/parse-queries.d.ts +13 -0
  19. package/dist/parse-queries.d.ts.map +1 -0
  20. package/dist/parse-queries.js +71 -0
  21. package/dist/parse-queries.js.map +1 -0
  22. package/dist/query-phase.d.ts +15 -0
  23. package/dist/query-phase.d.ts.map +1 -0
  24. package/dist/query-phase.js +81 -0
  25. package/dist/query-phase.js.map +1 -0
  26. package/dist/research-loop.d.ts +18 -0
  27. package/dist/research-loop.d.ts.map +1 -0
  28. package/dist/research-loop.js +357 -0
  29. package/dist/research-loop.js.map +1 -0
  30. package/dist/synthesis-phase.d.ts +8 -0
  31. package/dist/synthesis-phase.d.ts.map +1 -0
  32. package/dist/synthesis-phase.js +21 -0
  33. package/dist/synthesis-phase.js.map +1 -0
  34. package/package.json +73 -0
  35. package/src/approval.test.ts +82 -0
  36. package/src/approval.ts +105 -0
  37. package/src/constants.ts +153 -0
  38. package/src/fanout-phase.ts +177 -0
  39. package/src/index.test.ts +575 -0
  40. package/src/index.ts +27 -0
  41. package/src/parse-queries.ts +72 -0
  42. package/src/query-phase.ts +123 -0
  43. package/src/research-loop.ts +414 -0
  44. package/src/synthesis-phase.ts +25 -0
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Parse the planner's output into individual sub-question strings — a
3
+ * numbered list under a QUERIES: header.
4
+ */
5
+ export function parseQueries(text) {
6
+ return parseNumberedBlock(text, /^queries\s*:?$/i);
7
+ }
8
+ /**
9
+ * Parse the follow-up planner output. Accepts either a numbered list
10
+ * under `FOLLOWUPS:` OR the literal `FOLLOWUPS: (none)` form. Returns
11
+ * an empty array in both the "(none)" and "no parseable items" cases —
12
+ * the loop treats both as "no more research needed, proceed to synthesis".
13
+ */
14
+ export function parseFollowups(text) {
15
+ const items = parseNumberedBlock(text, /^followups\s*:?$/i);
16
+ // "(none)" sentinel — the model is telling us no follow-ups are needed.
17
+ // Anchor it to a full line so a parenthetical that merely follows the
18
+ // header (e.g. "FOLLOWUPS:\n(none of the prior sources covered cost)\n1. …")
19
+ // doesn't swallow a genuine numbered list below it. Only honor the sentinel
20
+ // when no numbered items were parsed.
21
+ if (items.length === 0 && /^followups\s*:\s*\(none\)\s*$/im.test(text))
22
+ return [];
23
+ return items;
24
+ }
25
+ /**
26
+ * Cap on a single joined query/follow-up. A hostile or malformed model could
27
+ * emit thousands of continuation lines that all attach to one item; bound the
28
+ * per-item growth so a mangled plan can't produce a multi-MB subagent prompt.
29
+ */
30
+ const MAX_ITEM_CHARS = 2000;
31
+ function parseNumberedBlock(text, headerRegex) {
32
+ const lines = text.split('\n');
33
+ const items = [];
34
+ // Tracks whether the previous meaningful line was (or extended) a list item,
35
+ // so we only ever join wrapped continuations onto a real item.
36
+ let continuationOpen = false;
37
+ for (const raw of lines) {
38
+ const line = raw.trim();
39
+ // A blank line ends the current item's continuation run: a wrapped query
40
+ // never spans a blank line, so this prevents an unrelated trailing
41
+ // paragraph from being glued onto the last query.
42
+ if (!line) {
43
+ continuationOpen = false;
44
+ continue;
45
+ }
46
+ if (headerRegex.test(line)) {
47
+ // A header also closes any open continuation run.
48
+ continuationOpen = false;
49
+ continue;
50
+ }
51
+ const m = /^(?:\d+[.)]|[-*•])\s*(.+)$/.exec(line);
52
+ if (m) {
53
+ items.push(m[1].trim());
54
+ continuationOpen = true;
55
+ continue;
56
+ }
57
+ // Non-list line. If it directly continues an open item (the model wrapped a
58
+ // long query across lines), join it on rather than silently truncating the
59
+ // query — a dropped continuation gets sent to a subagent as a half-question.
60
+ // A non-list line with NO open item (junk preamble, a "(none …)" header
61
+ // parenthetical, etc.) is still dropped.
62
+ if (continuationOpen && items.length > 0) {
63
+ const last = items[items.length - 1];
64
+ const joined = `${last} ${line}`;
65
+ items[items.length - 1] =
66
+ joined.length > MAX_ITEM_CHARS ? joined.slice(0, MAX_ITEM_CHARS) : joined;
67
+ }
68
+ }
69
+ return items;
70
+ }
71
+ //# sourceMappingURL=parse-queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-queries.js","sourceRoot":"","sources":["../src/parse-queries.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;IAC5D,wEAAwE;IACxE,sEAAsE;IACtE,6EAA6E;IAC7E,4EAA4E;IAC5E,sCAAsC;IACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAClF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,SAAS,kBAAkB,CAAC,IAAY,EAAE,WAAmB;IAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,6EAA6E;IAC7E,+DAA+D;IAC/D,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,yEAAyE;QACzE,mEAAmE;QACnE,kDAAkD;QAClD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,gBAAgB,GAAG,KAAK,CAAC;YACzB,SAAS;QACX,CAAC;QACD,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,kDAAkD;YAClD,gBAAgB,GAAG,KAAK,CAAC;YACzB,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,EAAE,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YACzB,gBAAgB,GAAG,IAAI,CAAC;YACxB,SAAS;QACX,CAAC;QACD,4EAA4E;QAC5E,2EAA2E;QAC3E,6EAA6E;QAC7E,wEAAwE;QACxE,yCAAyC;QACzC,IAAI,gBAAgB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;YACtC,MAAM,MAAM,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;YACjC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;gBACrB,MAAM,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAC9E,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type ModeContext } from '@moxxy/sdk';
2
+ import type { RoundFinding } from './fanout-phase.js';
3
+ /**
4
+ * Drive the initial query-planning turn. Single-shot stream — the
5
+ * planner shouldn't be web-searching, that's the subagents' job.
6
+ * Returns the raw text, or null on provider error (already emitted).
7
+ */
8
+ export declare function collectQueryPlan(ctx: ModeContext, redraftFeedback: string | null): Promise<string | null>;
9
+ /**
10
+ * Drive the follow-up planning turn between gather rounds. The model
11
+ * sees the prior round(s)' findings as context and decides whether to
12
+ * spawn more parallel research or move to synthesis.
13
+ */
14
+ export declare function collectFollowupPlan(ctx: ModeContext, originalPrompt: string, priorFindings: ReadonlyArray<RoundFinding>): Promise<string | null>;
15
+ //# sourceMappingURL=query-phase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-phase.d.ts","sourceRoot":"","sources":["../src/query-phase.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,YAAY,CAAC;AAMpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,eAAe,EAAE,MAAM,GAAG,IAAI,GAC7B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,WAAW,EAChB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,GACzC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB"}
@@ -0,0 +1,81 @@
1
+ import { buildSystemPromptWithSkills, runSingleShotTurn, } from '@moxxy/sdk';
2
+ import { FOLLOWUP_PLAN_SYSTEM_PROMPT, QUERY_PLAN_SYSTEM_PROMPT, } from './constants.js';
3
+ /**
4
+ * Drive the initial query-planning turn. Single-shot stream — the
5
+ * planner shouldn't be web-searching, that's the subagents' job.
6
+ * Returns the raw text, or null on provider error (already emitted).
7
+ */
8
+ export async function collectQueryPlan(ctx, redraftFeedback) {
9
+ const messages = buildPlannerMessages(ctx, QUERY_PLAN_SYSTEM_PROMPT, buildInitialUserMessages(ctx, redraftFeedback));
10
+ return runSingleShotTurn(ctx, messages, { maxTokens: 800 });
11
+ }
12
+ /**
13
+ * Drive the follow-up planning turn between gather rounds. The model
14
+ * sees the prior round(s)' findings as context and decides whether to
15
+ * spawn more parallel research or move to synthesis.
16
+ */
17
+ export async function collectFollowupPlan(ctx, originalPrompt, priorFindings) {
18
+ const messages = buildPlannerMessages(ctx, FOLLOWUP_PLAN_SYSTEM_PROMPT, buildFollowupUserMessages(originalPrompt, priorFindings));
19
+ return runSingleShotTurn(ctx, messages, { maxTokens: 800 });
20
+ }
21
+ function buildPlannerMessages(ctx, systemPrompt, userMessages) {
22
+ const systemWithSkills = buildSystemPromptWithSkills(ctx.systemPrompt, ctx.skills.list()) ?? '';
23
+ return [
24
+ {
25
+ role: 'system',
26
+ content: [
27
+ {
28
+ type: 'text',
29
+ text: systemPrompt + (systemWithSkills ? `\n\n${systemWithSkills}` : ''),
30
+ },
31
+ ],
32
+ },
33
+ ...userMessages,
34
+ ];
35
+ }
36
+ function buildInitialUserMessages(ctx, redraftFeedback) {
37
+ const out = [];
38
+ for (const e of ctx.log.slice()) {
39
+ if (e.type === 'user_prompt') {
40
+ out.push({ role: 'user', content: [{ type: 'text', text: e.text }] });
41
+ }
42
+ }
43
+ if (redraftFeedback) {
44
+ out.push({
45
+ role: 'user',
46
+ content: [
47
+ {
48
+ type: 'text',
49
+ text: `The previous query plan needs to be redrafted. Feedback from the user: ${redraftFeedback}\n\n` +
50
+ `Produce a new QUERIES block addressing this feedback.`,
51
+ },
52
+ ],
53
+ });
54
+ }
55
+ return out;
56
+ }
57
+ function buildFollowupUserMessages(originalPrompt, priorFindings) {
58
+ const sections = [];
59
+ sections.push(`Original question:\n${originalPrompt}`);
60
+ sections.push('');
61
+ sections.push('Prior research findings:');
62
+ for (const f of priorFindings) {
63
+ sections.push('');
64
+ sections.push(`### Round ${f.round}, sub-question: ${f.question}`);
65
+ if (f.error) {
66
+ sections.push(`(errored: ${f.error})`);
67
+ }
68
+ else {
69
+ sections.push(f.text.trim() || '(empty response)');
70
+ }
71
+ }
72
+ sections.push('');
73
+ sections.push('Decide whether more parallel research is needed. Reply with a FOLLOWUPS: block as instructed.');
74
+ return [
75
+ {
76
+ role: 'user',
77
+ content: [{ type: 'text', text: sections.join('\n') }],
78
+ },
79
+ ];
80
+ }
81
+ //# sourceMappingURL=query-phase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-phase.js","sourceRoot":"","sources":["../src/query-phase.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { ModeContext, MoxxyEvent } from '@moxxy/sdk';
2
+ /**
3
+ * Deep-research loop:
4
+ * 1. Plan QUERIES: block (with redraft-able approval gate)
5
+ * 2. Fan out (round 1) — one subagent per gathering query, in parallel
6
+ * 3. Up to MAX_FOLLOWUP_ROUNDS of:
7
+ * a. Ask model "given findings so far, do you need follow-ups?"
8
+ * b. If yes, fan out follow-ups with the prior findings as
9
+ * context in each subagent's prompt.
10
+ * c. If no, break out.
11
+ * 4. Synthesis approval gate (digest of all rounds)
12
+ * 5. Synthesize the final cited writeup.
13
+ *
14
+ * Headless contexts auto-approve both gates. Missing ctx.subagents is
15
+ * fatal — fan-out is the whole point of this mode.
16
+ */
17
+ export declare function runDeepResearchMode(ctx: ModeContext): AsyncIterable<MoxxyEvent>;
18
+ //# sourceMappingURL=research-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"research-loop.d.ts","sourceRoot":"","sources":["../src/research-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAyB1D;;;;;;;;;;;;;;GAcG;AACH,wBAAuB,mBAAmB,CAAC,GAAG,EAAE,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,CA8HtF"}
@@ -0,0 +1,357 @@
1
+ import { runQueryApprovalGate, runSynthesisApprovalGate, } from './approval.js';
2
+ import { RESEARCH_MODE_NAME, DEEP_RESEARCH_PLUGIN_ID, MAX_FOLLOWUPS_PER_ROUND, MAX_FOLLOWUP_ROUNDS, MAX_REDRAFTS, MAX_SUBAGENTS, } from './constants.js';
3
+ import { buildFanoutDigest, buildSynthesisInput, flattenOutcome, runFanout, } from './fanout-phase.js';
4
+ import { parseFollowups, parseQueries } from './parse-queries.js';
5
+ import { collectFollowupPlan, collectQueryPlan } from './query-phase.js';
6
+ import { collectSynthesis } from './synthesis-phase.js';
7
+ /**
8
+ * Deep-research loop:
9
+ * 1. Plan QUERIES: block (with redraft-able approval gate)
10
+ * 2. Fan out (round 1) — one subagent per gathering query, in parallel
11
+ * 3. Up to MAX_FOLLOWUP_ROUNDS of:
12
+ * a. Ask model "given findings so far, do you need follow-ups?"
13
+ * b. If yes, fan out follow-ups with the prior findings as
14
+ * context in each subagent's prompt.
15
+ * c. If no, break out.
16
+ * 4. Synthesis approval gate (digest of all rounds)
17
+ * 5. Synthesize the final cited writeup.
18
+ *
19
+ * Headless contexts auto-approve both gates. Missing ctx.subagents is
20
+ * fatal — fan-out is the whole point of this mode.
21
+ */
22
+ export async function* runDeepResearchMode(ctx) {
23
+ if (ctx.signal.aborted) {
24
+ yield await ctx.emit({
25
+ type: 'abort',
26
+ sessionId: ctx.sessionId,
27
+ turnId: ctx.turnId,
28
+ source: 'system',
29
+ reason: 'aborted before deep-research start',
30
+ });
31
+ return;
32
+ }
33
+ if (!ctx.subagents) {
34
+ yield await ctx.emit({
35
+ type: 'error',
36
+ sessionId: ctx.sessionId,
37
+ turnId: ctx.turnId,
38
+ source: 'system',
39
+ kind: 'fatal',
40
+ message: 'research: ctx.subagents is unavailable (the @moxxy/plugin-subagents plugin appears disabled). ' +
41
+ 'Re-enable it, or switch to the default mode for a serial alternative.',
42
+ });
43
+ return;
44
+ }
45
+ yield await ctx.emit({
46
+ type: 'mode_iteration',
47
+ sessionId: ctx.sessionId,
48
+ turnId: ctx.turnId,
49
+ source: 'system',
50
+ strategy: RESEARCH_MODE_NAME,
51
+ iteration: 0,
52
+ routing: 'unresolved',
53
+ });
54
+ // Phase 1: query plan (with redraftable gate).
55
+ const planning = yield* runPlanningPhase(ctx);
56
+ if (planning === null)
57
+ return;
58
+ const { queries, originalPrompt } = planning;
59
+ // Phase 2: round-1 fan out.
60
+ const allFindings = [];
61
+ const round1 = yield* runRound(ctx, 1, queries, []);
62
+ if (round1 === null)
63
+ return;
64
+ allFindings.push(...round1);
65
+ // Phase 3: follow-up rounds, capped to MAX_FOLLOWUP_ROUNDS.
66
+ for (let round = 2; round <= MAX_FOLLOWUP_ROUNDS + 1; round += 1) {
67
+ if (ctx.signal.aborted)
68
+ return;
69
+ const followups = yield* runFollowupPlan(ctx, originalPrompt, allFindings, round);
70
+ if (followups === null)
71
+ return;
72
+ if (followups.length === 0) {
73
+ yield await ctx.emit({
74
+ type: 'plugin_event',
75
+ sessionId: ctx.sessionId,
76
+ turnId: ctx.turnId,
77
+ source: 'plugin',
78
+ pluginId: DEEP_RESEARCH_PLUGIN_ID,
79
+ subtype: 'deep_research_followups_none',
80
+ payload: { round },
81
+ });
82
+ break;
83
+ }
84
+ const nextRound = yield* runRound(ctx, round, followups, allFindings);
85
+ if (nextRound === null)
86
+ return;
87
+ allFindings.push(...nextRound);
88
+ }
89
+ if (ctx.signal.aborted)
90
+ return;
91
+ // Phase 4: synthesis approval gate.
92
+ const digest = buildFanoutDigest(allFindings);
93
+ const gate = await runSynthesisApprovalGate(ctx, digest);
94
+ if (gate.kind === 'cancel') {
95
+ yield await ctx.emit({
96
+ type: 'abort',
97
+ sessionId: ctx.sessionId,
98
+ turnId: ctx.turnId,
99
+ source: 'user',
100
+ reason: 'synthesis cancelled by user',
101
+ });
102
+ return;
103
+ }
104
+ // Phase 5: synthesize.
105
+ const synthesisInput = buildSynthesisInput(originalPrompt, allFindings);
106
+ const synthesisText = await collectSynthesis(ctx, synthesisInput);
107
+ if (synthesisText === null)
108
+ return;
109
+ // Synthesis is a multi-second provider call; if the user aborted while it ran,
110
+ // emit an abort rather than the finished report — matching the abort discipline
111
+ // every other phase in this loop follows (see the ctx.signal.aborted guards above).
112
+ if (ctx.signal.aborted) {
113
+ yield await ctx.emit({
114
+ type: 'abort',
115
+ sessionId: ctx.sessionId,
116
+ turnId: ctx.turnId,
117
+ source: 'system',
118
+ reason: 'aborted during synthesis',
119
+ });
120
+ return;
121
+ }
122
+ yield await ctx.emit({
123
+ type: 'assistant_message',
124
+ sessionId: ctx.sessionId,
125
+ turnId: ctx.turnId,
126
+ source: 'model',
127
+ content: synthesisText,
128
+ stopReason: 'end_turn',
129
+ });
130
+ yield await ctx.emit({
131
+ type: 'plugin_event',
132
+ sessionId: ctx.sessionId,
133
+ turnId: ctx.turnId,
134
+ source: 'plugin',
135
+ pluginId: DEEP_RESEARCH_PLUGIN_ID,
136
+ subtype: 'deep_research_synthesis_completed',
137
+ payload: {
138
+ totalFindings: allFindings.length,
139
+ rounds: Math.max(...allFindings.map((f) => f.round), 1),
140
+ errored: allFindings.filter((f) => f.error).length,
141
+ },
142
+ });
143
+ }
144
+ /**
145
+ * One round of parallel fan-out — emits start/complete events around
146
+ * runFanout. Returns the per-round findings, or null on abort.
147
+ */
148
+ async function* runRound(ctx, round, queries, priorFindings) {
149
+ yield await ctx.emit({
150
+ type: 'plugin_event',
151
+ sessionId: ctx.sessionId,
152
+ turnId: ctx.turnId,
153
+ source: 'plugin',
154
+ pluginId: DEEP_RESEARCH_PLUGIN_ID,
155
+ subtype: 'deep_research_fanout_started',
156
+ payload: { round, queries: queries.length },
157
+ });
158
+ const outcome = await runFanout(ctx, queries, priorFindings);
159
+ yield await ctx.emit({
160
+ type: 'plugin_event',
161
+ sessionId: ctx.sessionId,
162
+ turnId: ctx.turnId,
163
+ source: 'plugin',
164
+ pluginId: DEEP_RESEARCH_PLUGIN_ID,
165
+ subtype: 'deep_research_fanout_completed',
166
+ payload: {
167
+ round,
168
+ total: queries.length,
169
+ errored: outcome.errored.length,
170
+ },
171
+ });
172
+ if (ctx.signal.aborted)
173
+ return null;
174
+ return flattenOutcome(round, queries, outcome);
175
+ }
176
+ /**
177
+ * Ask the model whether to fan out more queries, given everything
178
+ * gathered so far. Returns the parsed follow-up queries (may be empty)
179
+ * or null on a fatal error.
180
+ */
181
+ async function* runFollowupPlan(ctx, originalPrompt, priorFindings, round) {
182
+ yield await ctx.emit({
183
+ type: 'plugin_event',
184
+ sessionId: ctx.sessionId,
185
+ turnId: ctx.turnId,
186
+ source: 'plugin',
187
+ pluginId: DEEP_RESEARCH_PLUGIN_ID,
188
+ subtype: 'deep_research_followups_planning',
189
+ payload: { round, basedOn: priorFindings.length },
190
+ });
191
+ const text = await collectFollowupPlan(ctx, originalPrompt, priorFindings);
192
+ if (text === null)
193
+ return null;
194
+ const followups = parseFollowups(text);
195
+ // Apply cap and surface the trim as an event — but only when there's
196
+ // something to draft. The empty case is signalled by the caller via
197
+ // `deep_research_followups_none`; emitting a "drafted with kept=0"
198
+ // event here would be a duplicate "nothing to do" signal.
199
+ const trimmed = followups.slice(0, MAX_FOLLOWUPS_PER_ROUND);
200
+ if (trimmed.length === 0)
201
+ return [];
202
+ yield await ctx.emit({
203
+ type: 'plugin_event',
204
+ sessionId: ctx.sessionId,
205
+ turnId: ctx.turnId,
206
+ source: 'plugin',
207
+ pluginId: DEEP_RESEARCH_PLUGIN_ID,
208
+ subtype: 'deep_research_followups_drafted',
209
+ payload: {
210
+ round,
211
+ proposed: followups.length,
212
+ kept: trimmed.length,
213
+ queries: trimmed,
214
+ },
215
+ });
216
+ // Materialize the follow-up plan as an assistant_message so the user
217
+ // sees the queries that are about to run, even without an approval
218
+ // gate. (We deliberately skip a per-round approval gate to keep the
219
+ // user-facing checkpoints lightweight — the synthesis gate at the
220
+ // end is the final go/no-go.)
221
+ yield await ctx.emit({
222
+ type: 'assistant_message',
223
+ sessionId: ctx.sessionId,
224
+ turnId: ctx.turnId,
225
+ source: 'model',
226
+ content: `Follow-up round ${round} — spawning ${trimmed.length} more subagent${trimmed.length === 1 ? '' : 's'}:\n` +
227
+ trimmed.map((q, i) => `${i + 1}. ${q}`).join('\n'),
228
+ stopReason: 'end_turn',
229
+ });
230
+ return trimmed;
231
+ }
232
+ async function* runPlanningPhase(ctx) {
233
+ // Capture the original prompt from the log so we can re-include it in
234
+ // synthesis context. Use the LAST user_prompt — the one that triggered this
235
+ // turn — to stay consistent with how the runner scopes a turn (a multi-turn
236
+ // session's earlier prompts would otherwise anchor synthesis on a stale
237
+ // question). null = no user prompt at all (empty log).
238
+ const originalPrompt = (() => {
239
+ const events = ctx.log.slice();
240
+ for (let i = events.length - 1; i >= 0; i -= 1) {
241
+ const e = events[i];
242
+ if (e.type === 'user_prompt')
243
+ return e.text;
244
+ }
245
+ return null;
246
+ })();
247
+ if (originalPrompt === null) {
248
+ yield await ctx.emit({
249
+ type: 'error',
250
+ sessionId: ctx.sessionId,
251
+ turnId: ctx.turnId,
252
+ source: 'system',
253
+ kind: 'fatal',
254
+ message: 'deep-research: no user prompt found in the session log — nothing to research.',
255
+ });
256
+ return null;
257
+ }
258
+ let redraftFeedback = null;
259
+ let redraftCount = 0;
260
+ while (true) {
261
+ if (ctx.signal.aborted) {
262
+ yield await ctx.emit({
263
+ type: 'abort',
264
+ sessionId: ctx.sessionId,
265
+ turnId: ctx.turnId,
266
+ source: 'system',
267
+ reason: 'aborted during planning',
268
+ });
269
+ return null;
270
+ }
271
+ const planText = await collectQueryPlan(ctx, redraftFeedback);
272
+ if (planText === null)
273
+ return null;
274
+ const queries = parseQueries(planText);
275
+ yield await ctx.emit({
276
+ type: 'plugin_event',
277
+ sessionId: ctx.sessionId,
278
+ turnId: ctx.turnId,
279
+ source: 'plugin',
280
+ pluginId: DEEP_RESEARCH_PLUGIN_ID,
281
+ subtype: 'deep_research_queries_drafted',
282
+ payload: { text: planText, queries, redraft: redraftCount },
283
+ });
284
+ if (queries.length === 0) {
285
+ yield await ctx.emit({
286
+ type: 'assistant_message',
287
+ sessionId: ctx.sessionId,
288
+ turnId: ctx.turnId,
289
+ source: 'model',
290
+ content: planText,
291
+ stopReason: 'end_turn',
292
+ });
293
+ return null;
294
+ }
295
+ if (queries.length > MAX_SUBAGENTS) {
296
+ // Over-production is a normal, recoverable failure mode ("be exhaustive"
297
+ // prompts trip it easily). When an interactive resolver is present and we
298
+ // still have redraft budget, auto-redraft asking the planner to narrow,
299
+ // rather than killing the whole turn. Headless (no approval) or once the
300
+ // redraft cap is exhausted, fall through to the fatal abort.
301
+ const nextCount = redraftCount + 1;
302
+ if (ctx.approval && nextCount <= MAX_REDRAFTS) {
303
+ redraftCount = nextCount;
304
+ redraftFeedback =
305
+ `You produced ${queries.length} queries; the cap is ${MAX_SUBAGENTS}. ` +
306
+ `Return at most ${MAX_SUBAGENTS} parallel gathering queries.`;
307
+ continue;
308
+ }
309
+ yield await ctx.emit({
310
+ type: 'error',
311
+ sessionId: ctx.sessionId,
312
+ turnId: ctx.turnId,
313
+ source: 'system',
314
+ kind: 'fatal',
315
+ message: `deep-research: refusing a ${queries.length}-query plan (cap is ${MAX_SUBAGENTS}). Narrow scope or rephrase.`,
316
+ });
317
+ return null;
318
+ }
319
+ const gate = await runQueryApprovalGate(ctx, planText, queries.length, redraftCount);
320
+ redraftCount = gate.redraftCount;
321
+ if (gate.outcome.kind === 'cancel') {
322
+ yield await ctx.emit({
323
+ type: 'abort',
324
+ sessionId: ctx.sessionId,
325
+ turnId: ctx.turnId,
326
+ source: 'user',
327
+ reason: 'query plan rejected by user',
328
+ });
329
+ return null;
330
+ }
331
+ if (gate.outcome.kind === 'redraft-cap-exceeded') {
332
+ yield await ctx.emit({
333
+ type: 'error',
334
+ sessionId: ctx.sessionId,
335
+ turnId: ctx.turnId,
336
+ source: 'system',
337
+ kind: 'fatal',
338
+ message: `deep-research: redrafted ${MAX_REDRAFTS}× without approval; aborting.`,
339
+ });
340
+ return null;
341
+ }
342
+ if (gate.outcome.kind === 'redraft') {
343
+ redraftFeedback = gate.outcome.feedback;
344
+ continue;
345
+ }
346
+ yield await ctx.emit({
347
+ type: 'assistant_message',
348
+ sessionId: ctx.sessionId,
349
+ turnId: ctx.turnId,
350
+ source: 'model',
351
+ content: planText,
352
+ stopReason: 'end_turn',
353
+ });
354
+ return { queries, originalPrompt };
355
+ }
356
+ }
357
+ //# sourceMappingURL=research-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"research-loop.js","sourceRoot":"","sources":["../src/research-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type ModeContext } from '@moxxy/sdk';
2
+ /**
3
+ * Run the synthesis turn: single-shot stream that consumes the
4
+ * per-subagent findings and produces the final structured writeup.
5
+ * Returns the assembled text or null on error.
6
+ */
7
+ export declare function collectSynthesis(ctx: ModeContext, inputBody: string): Promise<string | null>;
8
+ //# sourceMappingURL=synthesis-phase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthesis-phase.d.ts","sourceRoot":"","sources":["../src/synthesis-phase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,WAAW,EAAwB,MAAM,YAAY,CAAC;AAIvF;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB"}
@@ -0,0 +1,21 @@
1
+ import { runSingleShotTurn } from '@moxxy/sdk';
2
+ import { SYNTHESIS_SYSTEM_PROMPT } from './constants.js';
3
+ /**
4
+ * Run the synthesis turn: single-shot stream that consumes the
5
+ * per-subagent findings and produces the final structured writeup.
6
+ * Returns the assembled text or null on error.
7
+ */
8
+ export async function collectSynthesis(ctx, inputBody) {
9
+ const messages = [
10
+ {
11
+ role: 'system',
12
+ content: [{ type: 'text', text: SYNTHESIS_SYSTEM_PROMPT }],
13
+ },
14
+ {
15
+ role: 'user',
16
+ content: [{ type: 'text', text: inputBody }],
17
+ },
18
+ ];
19
+ return runSingleShotTurn(ctx, messages, { maxTokens: 4096 });
20
+ }
21
+ //# sourceMappingURL=synthesis-phase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthesis-phase.js","sourceRoot":"","sources":["../src/synthesis-phase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA0C,MAAM,YAAY,CAAC;AAEvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAgB,EAChB,SAAiB;IAEjB,MAAM,QAAQ,GAAsB;QAClC;YACE,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;SAC3D;QACD;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;SAC7C;KACF,CAAC;IACF,OAAO,iBAAiB,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/D,CAAC"}