maka-agent 0.2.0-dev.43.20260919 → 0.2.0-dev.45.20260921
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/LICENSE +12 -6
- package/dist/mcp-capability-provider.js +7 -0
- package/dist/runtime-host-service-manager.js +1 -6
- package/dist/runtime-host-tui-command.js +1 -1
- package/dist/runtime-host-update-command.js +39 -27
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/artifacts.js +1 -0
- package/node_modules/@maka/core/dist/chat-model-choice.js +3 -0
- package/node_modules/@maka/core/dist/llm-connections.js +3 -21
- package/node_modules/@maka/core/dist/model-catalog.js +8 -20
- package/node_modules/@maka/core/dist/model-metadata.generated.js +304 -304
- package/node_modules/@maka/core/dist/model-metadata.js +1 -2
- package/node_modules/@maka/core/dist/model-thinking.js +16 -1
- package/node_modules/@maka/core/dist/models-dev-projection.js +0 -1
- package/node_modules/@maka/core/dist/provider-auth.js +1 -7
- package/node_modules/@maka/core/dist/provider-registry.js +20 -58
- package/node_modules/@maka/core/dist/recall.js +5 -1
- package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +7 -0
- package/node_modules/@maka/core/dist/runtime-policy/model-catalog-entry-codec.js +14 -1
- package/node_modules/@maka/core/dist/session-start-mode.js +2 -5
- package/node_modules/@maka/core/dist/session.js +0 -1
- package/node_modules/@maka/core/dist/settings.js +19 -3
- package/node_modules/@maka/core/dist/tool-quiet-preview.js +2 -16
- package/node_modules/@maka/core/dist/transcript-search.js +46 -0
- package/node_modules/@maka/core/package.json +1 -4
- package/node_modules/@maka/mcp/dist/index.js +22 -0
- package/node_modules/@maka/mcp/dist/oauth.js +4 -2
- package/node_modules/@maka/mcp/dist/tool-progress.js +45 -0
- package/node_modules/@maka/runtime/dist/model-factory.js +0 -16
- package/node_modules/@maka/runtime/dist/model-fetcher.js +1 -4
- package/node_modules/@maka/runtime/dist/model-runtime.js +0 -8
- package/node_modules/@maka/runtime/dist/opencode-session-header.js +2 -3
- package/node_modules/@maka/runtime/dist/recall-tools.js +4 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +5 -57
- package/node_modules/@maka/runtime/dist/subagent-tools.js +69 -11
- package/node_modules/@maka/runtime/dist/test-connection.js +4 -114
- package/node_modules/@maka/runtime/package.json +0 -1
- package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +44 -9
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +25 -18
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +3 -1
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +33 -3
- package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +0 -108
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +12 -1
- package/node_modules/@maka/runtime-host/dist/protocol/message.js +10 -0
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +4 -6
- package/node_modules/@maka/runtime-host/dist/protocol/recall.js +270 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +1 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -7
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +0 -33
- package/node_modules/@maka/runtime-host/dist/protocol/usage-screen.js +12 -4
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/bootstrap-runtime-policy.js +31 -104
- package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +1 -57
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +11 -35
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +3 -20
- package/node_modules/@maka/runtime-host/dist/server/interactive-turn-coordinator.js +1 -16
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +46 -28
- package/node_modules/@maka/runtime-host/dist/server/recall-coordinator.js +117 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +5 -16
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +21 -12
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +0 -4
- package/node_modules/@maka/runtime-host/dist/server/session-turn-access-request-coordinator.js +7 -17
- package/node_modules/@maka/storage/dist/artifact-metadata-codec.js +4 -3
- package/node_modules/@maka/storage/dist/artifact-store.js +1 -11
- package/node_modules/@maka/storage/dist/process-lifetime-file-update-lock.js +0 -41
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +0 -62
- package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +0 -31
- package/node_modules/@maka/storage/dist/runtime-policy-stores.js +0 -3
- package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +29 -5
- package/node_modules/@maka/storage/dist/sqlite-workflow-schema.js +2 -0
- package/node_modules/@maka/storage/dist/storage-writer-composition.js +0 -3
- package/node_modules/@maka/storage/dist/telemetry-file-schema.js +5 -3
- package/node_modules/@maka/storage/dist/usage-screen.js +5 -6
- package/node_modules/@maka/storage/dist/usage-stores.js +14 -4
- package/node_modules/@maka/storage/package.json +0 -2
- package/package.json +1 -1
- package/node_modules/@maka/core/dist/connection-usage.js +0 -27
- package/node_modules/@maka/core/dist/deep-research-client-progress.js +0 -132
- package/node_modules/@maka/core/dist/deep-research-run.js +0 -625
- package/node_modules/@maka/core/dist/deep-research.js +0 -208
- package/node_modules/@maka/core/dist/thread-search.js +0 -498
- package/node_modules/@maka/runtime/dist/commandcode-cli-language-model.js +0 -837
- package/node_modules/@maka/runtime/dist/connection-usage.js +0 -232
- package/node_modules/@maka/runtime/dist/deep-research-tools.js +0 -732
- package/node_modules/@maka/runtime-host/dist/protocol/deep-research.js +0 -233
- package/node_modules/@maka/runtime-host/dist/server/deep-research-coordinator.js +0 -116
- package/node_modules/@maka/storage/dist/deep-research-authority.js +0 -129
- package/node_modules/@maka/storage/dist/deep-research-store.js +0 -343
|
@@ -1,732 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
-
* or more contributor license agreements. See the NOTICE file
|
|
4
|
-
* distributed with this work for additional information
|
|
5
|
-
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
-
* to you under the Apache License, Version 2.0 (the
|
|
7
|
-
* "License"); you may not use this file except in compliance
|
|
8
|
-
* with the License. You may obtain a copy of the License at
|
|
9
|
-
*
|
|
10
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
*
|
|
12
|
-
* Unless required by applicable law or agreed to in writing,
|
|
13
|
-
* software distributed under the License is distributed on an
|
|
14
|
-
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
-
* KIND, either express or implied. See the License for the
|
|
16
|
-
* specific language governing permissions and limitations
|
|
17
|
-
* under the License.
|
|
18
|
-
*/
|
|
19
|
-
import { createHash } from 'node:crypto';
|
|
20
|
-
import { z } from 'zod';
|
|
21
|
-
import { DEEP_RESEARCH_ACTIVE_STAGES, DEEP_RESEARCH_ARTIFACT_NAME_MAX_CHARS, DEEP_RESEARCH_ARTIFACT_ROLES, DEEP_RESEARCH_CHECKPOINT_ITEM_MAX_CHARS, DEEP_RESEARCH_CHECKPOINT_ITEMS_MAX, DEEP_RESEARCH_CHECKPOINT_TEXT_MAX_CHARS, DEEP_RESEARCH_CHECKLIST_STATUSES, DEEP_RESEARCH_DEFAULT_CHECKLIST, DEEP_RESEARCH_INSPECTED_REF_KINDS, DEEP_RESEARCH_LOCATOR_MAX_CHARS, DEEP_RESEARCH_OBJECTIVE_MAX_CHARS, DEEP_RESEARCH_REFS_MAX, DEEP_RESEARCH_REPORT_SECTION_KEYS, DEEP_RESEARCH_SCOPE_LEVELS, DEEP_RESEARCH_STEP_KINDS, DEEP_RESEARCH_STEP_LIST_ITEMS_MAX, DEEP_RESEARCH_STEP_STATUSES, DEEP_RESEARCH_STEP_TEXT_MAX_CHARS, normalizeDeepResearchObjective, } from '@maka/core/deep-research-run';
|
|
22
|
-
import { redactSecrets } from '@maka/core/redaction';
|
|
23
|
-
export const DEEP_RESEARCH_START_TOOL_NAME = 'deep_research_start';
|
|
24
|
-
export const DEEP_RESEARCH_SAVE_ARTIFACT_TOOL_NAME = 'deep_research_save_artifact';
|
|
25
|
-
export const DEEP_RESEARCH_READ_ARTIFACT_TOOL_NAME = 'deep_research_read_artifact';
|
|
26
|
-
export const DEEP_RESEARCH_UPDATE_CHECKLIST_TOOL_NAME = 'deep_research_update_checklist';
|
|
27
|
-
export const DEEP_RESEARCH_RECORD_STEP_TOOL_NAME = 'deep_research_record_step';
|
|
28
|
-
export const DEEP_RESEARCH_CHECKPOINT_TOOL_NAME = 'deep_research_checkpoint';
|
|
29
|
-
export const DEEP_RESEARCH_STATUS_TOOL_NAME = 'deep_research_status';
|
|
30
|
-
export const DEEP_RESEARCH_COMPLETE_TOOL_NAME = 'deep_research_complete';
|
|
31
|
-
const DEEP_RESEARCH_ALLOWED_TOOL_NAMES = new Set([
|
|
32
|
-
'AskUserQuestion',
|
|
33
|
-
'Read',
|
|
34
|
-
'Glob',
|
|
35
|
-
'Grep',
|
|
36
|
-
'WebSearch',
|
|
37
|
-
DEEP_RESEARCH_START_TOOL_NAME,
|
|
38
|
-
DEEP_RESEARCH_SAVE_ARTIFACT_TOOL_NAME,
|
|
39
|
-
DEEP_RESEARCH_READ_ARTIFACT_TOOL_NAME,
|
|
40
|
-
DEEP_RESEARCH_UPDATE_CHECKLIST_TOOL_NAME,
|
|
41
|
-
DEEP_RESEARCH_RECORD_STEP_TOOL_NAME,
|
|
42
|
-
DEEP_RESEARCH_CHECKPOINT_TOOL_NAME,
|
|
43
|
-
DEEP_RESEARCH_STATUS_TOOL_NAME,
|
|
44
|
-
DEEP_RESEARCH_COMPLETE_TOOL_NAME,
|
|
45
|
-
]);
|
|
46
|
-
export const DEEP_RESEARCH_ARTIFACT_CONTENT_MAX_CHARS = 512_000;
|
|
47
|
-
export const DEEP_RESEARCH_ARTIFACT_READ_DEFAULT_CHARS = 32_000;
|
|
48
|
-
export const DEEP_RESEARCH_ARTIFACT_READ_MAX_CHARS = 64_000;
|
|
49
|
-
export const DEEP_RESEARCH_STATUS_ARTIFACTS_MAX = 100;
|
|
50
|
-
const stableIdSchema = z
|
|
51
|
-
.string()
|
|
52
|
-
.trim()
|
|
53
|
-
.min(1)
|
|
54
|
-
.max(128)
|
|
55
|
-
.regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, 'Expected a stable research, task, or artifact id.')
|
|
56
|
-
.refine((value) => redactSecrets(value) === value, 'Research references cannot contain secret-like values.');
|
|
57
|
-
export function buildDeepResearchTools(deps) {
|
|
58
|
-
return [
|
|
59
|
-
buildStartTool(deps),
|
|
60
|
-
buildSaveArtifactTool(deps),
|
|
61
|
-
buildReadArtifactTool(deps),
|
|
62
|
-
buildUpdateChecklistTool(deps),
|
|
63
|
-
buildRecordStepTool(deps),
|
|
64
|
-
buildCheckpointTool(deps),
|
|
65
|
-
buildStatusTool(deps),
|
|
66
|
-
buildCompleteTool(deps),
|
|
67
|
-
];
|
|
68
|
-
}
|
|
69
|
-
export function isDeepResearchToolAllowed(tool) {
|
|
70
|
-
return DEEP_RESEARCH_ALLOWED_TOOL_NAMES.has(tool.name);
|
|
71
|
-
}
|
|
72
|
-
function buildStartTool(deps) {
|
|
73
|
-
return {
|
|
74
|
-
name: DEEP_RESEARCH_START_TOOL_NAME,
|
|
75
|
-
displayName: 'Initialize Research Workspace',
|
|
76
|
-
description: 'Initialize the durable Deep Research workspace for this session. Call once before archiving sources, ' +
|
|
77
|
-
'writing evidence notes, or checkpointing. Retrying the same tool call is safe.',
|
|
78
|
-
parameters: z.object({
|
|
79
|
-
objective: z
|
|
80
|
-
.string()
|
|
81
|
-
.trim()
|
|
82
|
-
.min(1)
|
|
83
|
-
.max(DEEP_RESEARCH_OBJECTIVE_MAX_CHARS)
|
|
84
|
-
.describe('The concrete research question and requested outcome.'),
|
|
85
|
-
scope_level: z
|
|
86
|
-
.enum(DEEP_RESEARCH_SCOPE_LEVELS)
|
|
87
|
-
.default('standard')
|
|
88
|
-
.describe('Research budget: quick, standard, or deep.'),
|
|
89
|
-
}),
|
|
90
|
-
impl: async (input, ctx) => {
|
|
91
|
-
const objective = normalizeDeepResearchObjective(input.objective);
|
|
92
|
-
if (!objective)
|
|
93
|
-
throw new Error('Deep Research objective is invalid');
|
|
94
|
-
const run = await deps.store.start(ctx.sessionId, objective, input.scope_level, mutationContext(ctx));
|
|
95
|
-
return renderRunStatus(run);
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
function buildReadArtifactTool(deps) {
|
|
100
|
-
return {
|
|
101
|
-
name: DEEP_RESEARCH_READ_ARTIFACT_TOOL_NAME,
|
|
102
|
-
displayName: 'Read Research Artifact',
|
|
103
|
-
description: 'Read a bounded chunk of a persisted artifact from this Deep Research workspace. ' +
|
|
104
|
-
'Use artifact ids from deep_research_status to recover evidence after interruption or restart.',
|
|
105
|
-
parameters: z.object({
|
|
106
|
-
artifact_id: stableIdSchema.describe('Research artifact id from the current workspace.'),
|
|
107
|
-
offset_chars: z
|
|
108
|
-
.number()
|
|
109
|
-
.int()
|
|
110
|
-
.min(0)
|
|
111
|
-
.max(DEEP_RESEARCH_ARTIFACT_CONTENT_MAX_CHARS)
|
|
112
|
-
.optional()
|
|
113
|
-
.describe('Zero-based character offset for chunked reads.'),
|
|
114
|
-
max_chars: z
|
|
115
|
-
.number()
|
|
116
|
-
.int()
|
|
117
|
-
.min(1)
|
|
118
|
-
.max(DEEP_RESEARCH_ARTIFACT_READ_MAX_CHARS)
|
|
119
|
-
.optional()
|
|
120
|
-
.describe(`Maximum characters to return (default ${DEEP_RESEARCH_ARTIFACT_READ_DEFAULT_CHARS}).`),
|
|
121
|
-
}),
|
|
122
|
-
impl: async (input, ctx) => {
|
|
123
|
-
const run = await deps.store.read(ctx.sessionId);
|
|
124
|
-
if (!run) {
|
|
125
|
-
throw new Error(`Call ${DEEP_RESEARCH_START_TOOL_NAME} before reading research artifacts`);
|
|
126
|
-
}
|
|
127
|
-
const ref = run.artifacts.find((artifact) => artifact.artifactId === input.artifact_id);
|
|
128
|
-
if (!ref)
|
|
129
|
-
throw new Error('Research artifact is not part of this session workspace');
|
|
130
|
-
const record = await deps.artifactStore.get(input.artifact_id);
|
|
131
|
-
if (!record || record.sessionId !== ctx.sessionId || record.source !== 'deep_research') {
|
|
132
|
-
throw new Error('Research artifact is missing, deleted, or belongs to another session');
|
|
133
|
-
}
|
|
134
|
-
const read = await deps.artifactStore.readText(input.artifact_id, {
|
|
135
|
-
maxBytes: DEEP_RESEARCH_ARTIFACT_CONTENT_MAX_CHARS * 4,
|
|
136
|
-
});
|
|
137
|
-
if (!read.ok)
|
|
138
|
-
throw new Error(`Research artifact could not be read: ${read.reason}`);
|
|
139
|
-
const contentHash = `sha256:${createHash('sha256').update(read.text).digest('hex')}`;
|
|
140
|
-
if (contentHash !== ref.contentHash) {
|
|
141
|
-
throw new Error('Research artifact content no longer matches the durable research ledger');
|
|
142
|
-
}
|
|
143
|
-
const offset = input.offset_chars ?? 0;
|
|
144
|
-
const maxChars = input.max_chars ?? DEEP_RESEARCH_ARTIFACT_READ_DEFAULT_CHARS;
|
|
145
|
-
const selected = [];
|
|
146
|
-
let total = 0;
|
|
147
|
-
for (const character of read.text) {
|
|
148
|
-
if (total >= offset && total < offset + maxChars)
|
|
149
|
-
selected.push(character);
|
|
150
|
-
total += 1;
|
|
151
|
-
}
|
|
152
|
-
const end = Math.min(total, offset + maxChars);
|
|
153
|
-
const chunk = safeResearchArtifactContent(selected.join(''));
|
|
154
|
-
return [
|
|
155
|
-
`<deep-research-artifact id="${ref.artifactId}" role="${ref.role}" offset="${offset}" end="${end}" total="${total}">`,
|
|
156
|
-
`Name: ${normalizeInlineText(ref.name)}`,
|
|
157
|
-
...(ref.locator ? [`Locator: ${normalizeInlineText(ref.locator)}`] : []),
|
|
158
|
-
`Truncated: ${end < total}`,
|
|
159
|
-
'',
|
|
160
|
-
chunk,
|
|
161
|
-
'</deep-research-artifact>',
|
|
162
|
-
].join('\n');
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function buildSaveArtifactTool(deps) {
|
|
167
|
-
return {
|
|
168
|
-
name: DEEP_RESEARCH_SAVE_ARTIFACT_TOOL_NAME,
|
|
169
|
-
displayName: 'Save Research Artifact',
|
|
170
|
-
description: 'Persist a Markdown research artifact outside the model context. Archive raw source material as role=source ' +
|
|
171
|
-
'before writing derived evidence notes or report content. Derived artifacts must cite source artifact ids.',
|
|
172
|
-
parameters: z
|
|
173
|
-
.object({
|
|
174
|
-
role: z
|
|
175
|
-
.enum(DEEP_RESEARCH_ARTIFACT_ROLES)
|
|
176
|
-
.describe('Artifact role in the two-stage research workspace.'),
|
|
177
|
-
name: z
|
|
178
|
-
.string()
|
|
179
|
-
.trim()
|
|
180
|
-
.min(1)
|
|
181
|
-
.max(DEEP_RESEARCH_ARTIFACT_NAME_MAX_CHARS)
|
|
182
|
-
.describe('Human-readable Markdown filename.'),
|
|
183
|
-
content: z
|
|
184
|
-
.string()
|
|
185
|
-
.min(1)
|
|
186
|
-
.max(DEEP_RESEARCH_ARTIFACT_CONTENT_MAX_CHARS)
|
|
187
|
-
.describe('Exact Markdown body to persist.'),
|
|
188
|
-
summary: z
|
|
189
|
-
.string()
|
|
190
|
-
.trim()
|
|
191
|
-
.min(1)
|
|
192
|
-
.max(DEEP_RESEARCH_CHECKPOINT_TEXT_MAX_CHARS)
|
|
193
|
-
.describe('Short description shown in the artifact list.'),
|
|
194
|
-
locator: z
|
|
195
|
-
.string()
|
|
196
|
-
.trim()
|
|
197
|
-
.min(1)
|
|
198
|
-
.max(DEEP_RESEARCH_LOCATOR_MAX_CHARS)
|
|
199
|
-
.optional()
|
|
200
|
-
.describe('Required for source artifacts: URL, repository path, or other inspectable source locator.'),
|
|
201
|
-
source_artifact_ids: z
|
|
202
|
-
.array(stableIdSchema)
|
|
203
|
-
.max(DEEP_RESEARCH_REFS_MAX)
|
|
204
|
-
.optional()
|
|
205
|
-
.describe('Direct raw source artifact ids supporting this derived artifact.'),
|
|
206
|
-
report_section_key: z
|
|
207
|
-
.enum(DEEP_RESEARCH_REPORT_SECTION_KEYS)
|
|
208
|
-
.optional()
|
|
209
|
-
.describe('Required when role=report_section.'),
|
|
210
|
-
report_section_status: z
|
|
211
|
-
.enum(['drafted', 'completed'])
|
|
212
|
-
.optional()
|
|
213
|
-
.describe('Required when role=report_section.'),
|
|
214
|
-
})
|
|
215
|
-
.superRefine((input, ctx) => {
|
|
216
|
-
const sourceIds = input.source_artifact_ids ?? [];
|
|
217
|
-
if (input.role === 'source' && !input.locator) {
|
|
218
|
-
ctx.addIssue({
|
|
219
|
-
code: z.ZodIssueCode.custom,
|
|
220
|
-
path: ['locator'],
|
|
221
|
-
message: 'Source artifacts require a locator.',
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
if (input.role === 'source' && sourceIds.length > 0) {
|
|
225
|
-
ctx.addIssue({
|
|
226
|
-
code: z.ZodIssueCode.custom,
|
|
227
|
-
path: ['source_artifact_ids'],
|
|
228
|
-
message: 'Source artifacts cannot cite other research artifacts.',
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
if (input.role !== 'source' && sourceIds.length === 0) {
|
|
232
|
-
ctx.addIssue({
|
|
233
|
-
code: z.ZodIssueCode.custom,
|
|
234
|
-
path: ['source_artifact_ids'],
|
|
235
|
-
message: `${input.role} artifacts require direct source artifact ids.`,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
if (input.role === 'report_section' &&
|
|
239
|
-
(!input.report_section_key || !input.report_section_status)) {
|
|
240
|
-
ctx.addIssue({
|
|
241
|
-
code: z.ZodIssueCode.custom,
|
|
242
|
-
path: ['report_section_key'],
|
|
243
|
-
message: 'Report section artifacts require a section key and status.',
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
if (input.role !== 'report_section' &&
|
|
247
|
-
(input.report_section_key || input.report_section_status)) {
|
|
248
|
-
ctx.addIssue({
|
|
249
|
-
code: z.ZodIssueCode.custom,
|
|
250
|
-
path: ['report_section_key'],
|
|
251
|
-
message: 'Report section metadata is only valid for role=report_section.',
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
}),
|
|
255
|
-
impl: async (input, ctx) => {
|
|
256
|
-
const artifactId = stableArtifactId(ctx);
|
|
257
|
-
const sourceArtifactIds = dedupe(input.source_artifact_ids ?? []);
|
|
258
|
-
const inputHash = `sha256:${createHash('sha256').update(input.content).digest('hex')}`;
|
|
259
|
-
const replayEvent = await findToolCallEvent(deps.store, ctx.sessionId, ctx.toolCallId);
|
|
260
|
-
if (replayEvent) {
|
|
261
|
-
if (replayEvent.type !== 'research_artifact_recorded') {
|
|
262
|
-
throw new Error(`Deep Research tool call ${ctx.toolCallId} was already used for ${replayEvent.type}`);
|
|
263
|
-
}
|
|
264
|
-
const replay = replayEvent.artifact;
|
|
265
|
-
const replayRecord = await deps.artifactStore.get(replay.artifactId);
|
|
266
|
-
if (replay.artifactId !== artifactId ||
|
|
267
|
-
replay.role !== input.role ||
|
|
268
|
-
replay.name !== input.name ||
|
|
269
|
-
(replay.summary ?? replayRecord?.summary) !== input.summary ||
|
|
270
|
-
replay.locator !== input.locator ||
|
|
271
|
-
replay.contentHash !== inputHash ||
|
|
272
|
-
!sameStringArray(replay.sourceArtifactIds, sourceArtifactIds) ||
|
|
273
|
-
replay.reportSectionKey !== input.report_section_key ||
|
|
274
|
-
replay.reportSectionStatus !== input.report_section_status) {
|
|
275
|
-
throw new Error('Deep Research artifact tool call was retried with different content or metadata');
|
|
276
|
-
}
|
|
277
|
-
const replayRun = await deps.store.read(ctx.sessionId);
|
|
278
|
-
if (!replayRun)
|
|
279
|
-
throw new Error('Deep Research artifact replay is missing its workspace');
|
|
280
|
-
return `Research artifact ${artifactId} was already saved.\n${renderRunStatus(replayRun)}`;
|
|
281
|
-
}
|
|
282
|
-
await requireActiveRun(deps.store, ctx.sessionId);
|
|
283
|
-
const artifact = await deps.artifactStore.create({
|
|
284
|
-
sessionId: ctx.sessionId,
|
|
285
|
-
turnId: ctx.turnId,
|
|
286
|
-
name: input.name,
|
|
287
|
-
kind: 'file',
|
|
288
|
-
content: input.content,
|
|
289
|
-
mimeType: 'text/markdown',
|
|
290
|
-
source: 'deep_research',
|
|
291
|
-
summary: input.summary,
|
|
292
|
-
deepResearchRole: input.role,
|
|
293
|
-
id: artifactId,
|
|
294
|
-
});
|
|
295
|
-
let run;
|
|
296
|
-
try {
|
|
297
|
-
run = await deps.store.recordArtifact(ctx.sessionId, {
|
|
298
|
-
artifactId,
|
|
299
|
-
role: input.role,
|
|
300
|
-
name: input.name,
|
|
301
|
-
summary: input.summary,
|
|
302
|
-
createdAt: artifact.createdAt,
|
|
303
|
-
...(input.locator ? { locator: input.locator } : {}),
|
|
304
|
-
contentHash: inputHash,
|
|
305
|
-
sourceArtifactIds,
|
|
306
|
-
...(input.report_section_key ? { reportSectionKey: input.report_section_key } : {}),
|
|
307
|
-
...(input.report_section_status
|
|
308
|
-
? { reportSectionStatus: input.report_section_status }
|
|
309
|
-
: {}),
|
|
310
|
-
}, mutationContext(ctx));
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
await deps.artifactStore.delete(artifactId).catch(() => undefined);
|
|
314
|
-
throw error;
|
|
315
|
-
}
|
|
316
|
-
return `Saved ${input.role} artifact ${artifactId}.\n${renderRunStatus(run)}`;
|
|
317
|
-
},
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
function buildUpdateChecklistTool(deps) {
|
|
321
|
-
return {
|
|
322
|
-
name: DEEP_RESEARCH_UPDATE_CHECKLIST_TOOL_NAME,
|
|
323
|
-
displayName: 'Update Research Checklist',
|
|
324
|
-
description: 'Update one durable Deep Research checklist item. Completed items require saved evidence artifacts; ' +
|
|
325
|
-
'blocked items require a concrete blocker that remains visible after restart.',
|
|
326
|
-
parameters: z
|
|
327
|
-
.object({
|
|
328
|
-
item_id: stableIdSchema.refine((value) => DEEP_RESEARCH_DEFAULT_CHECKLIST.some((item) => item.itemId === value), 'Unknown Deep Research checklist item.'),
|
|
329
|
-
status: z.enum(DEEP_RESEARCH_CHECKLIST_STATUSES),
|
|
330
|
-
evidence_artifact_ids: z.array(stableIdSchema).max(DEEP_RESEARCH_REFS_MAX).optional(),
|
|
331
|
-
blocked_reason: z.string().trim().min(1).max(DEEP_RESEARCH_STEP_TEXT_MAX_CHARS).optional(),
|
|
332
|
-
})
|
|
333
|
-
.superRefine((input, ctx) => {
|
|
334
|
-
if (input.status === 'completed' && (input.evidence_artifact_ids?.length ?? 0) === 0) {
|
|
335
|
-
ctx.addIssue({
|
|
336
|
-
code: z.ZodIssueCode.custom,
|
|
337
|
-
path: ['evidence_artifact_ids'],
|
|
338
|
-
message: 'Completed checklist items require evidence artifacts.',
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
if (input.status === 'blocked' && !input.blocked_reason) {
|
|
342
|
-
ctx.addIssue({
|
|
343
|
-
code: z.ZodIssueCode.custom,
|
|
344
|
-
path: ['blocked_reason'],
|
|
345
|
-
message: 'Blocked checklist items require a reason.',
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
if (input.status !== 'blocked' && input.blocked_reason) {
|
|
349
|
-
ctx.addIssue({
|
|
350
|
-
code: z.ZodIssueCode.custom,
|
|
351
|
-
path: ['blocked_reason'],
|
|
352
|
-
message: 'A blocked reason is only valid for blocked checklist items.',
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
}),
|
|
356
|
-
impl: async (input, ctx) => {
|
|
357
|
-
await requireRun(deps.store, ctx.sessionId);
|
|
358
|
-
const run = await deps.store.updateChecklist(ctx.sessionId, {
|
|
359
|
-
itemId: input.item_id,
|
|
360
|
-
status: input.status,
|
|
361
|
-
evidenceArtifactIds: dedupe(input.evidence_artifact_ids ?? []),
|
|
362
|
-
...(input.blocked_reason ? { blockedReason: input.blocked_reason } : {}),
|
|
363
|
-
}, mutationContext(ctx));
|
|
364
|
-
return renderRunStatus(run);
|
|
365
|
-
},
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
function buildRecordStepTool(deps) {
|
|
369
|
-
const boundedText = z.string().trim().min(1).max(DEEP_RESEARCH_STEP_TEXT_MAX_CHARS);
|
|
370
|
-
const boundedList = z
|
|
371
|
-
.array(z.string().trim().min(1).max(DEEP_RESEARCH_LOCATOR_MAX_CHARS))
|
|
372
|
-
.max(DEEP_RESEARCH_STEP_LIST_ITEMS_MAX);
|
|
373
|
-
return {
|
|
374
|
-
name: DEEP_RESEARCH_RECORD_STEP_TOOL_NAME,
|
|
375
|
-
displayName: 'Record Research Step',
|
|
376
|
-
description: 'Record a bounded local-exploration or web-research step, including its search roots/query terms, ' +
|
|
377
|
-
'stopping condition, inspected references, worker runs, evidence, and any blocker.',
|
|
378
|
-
parameters: z
|
|
379
|
-
.object({
|
|
380
|
-
kind: z.enum(DEEP_RESEARCH_STEP_KINDS),
|
|
381
|
-
status: z.enum(DEEP_RESEARCH_STEP_STATUSES),
|
|
382
|
-
objective: boundedText,
|
|
383
|
-
summary: boundedText,
|
|
384
|
-
roots: boundedList.optional(),
|
|
385
|
-
keywords: boundedList.optional(),
|
|
386
|
-
ignored_paths: boundedList.optional(),
|
|
387
|
-
stopping_condition: boundedText,
|
|
388
|
-
expected_evidence: boundedText,
|
|
389
|
-
evidence_artifact_ids: z.array(stableIdSchema).max(DEEP_RESEARCH_REFS_MAX).optional(),
|
|
390
|
-
inspected_refs: z
|
|
391
|
-
.array(z.object({
|
|
392
|
-
kind: z.enum(DEEP_RESEARCH_INSPECTED_REF_KINDS),
|
|
393
|
-
locator: z.string().trim().min(1).max(DEEP_RESEARCH_LOCATOR_MAX_CHARS),
|
|
394
|
-
label: z.string().trim().min(1).max(DEEP_RESEARCH_STEP_TEXT_MAX_CHARS).optional(),
|
|
395
|
-
source_artifact_id: stableIdSchema.optional(),
|
|
396
|
-
}))
|
|
397
|
-
.max(DEEP_RESEARCH_STEP_LIST_ITEMS_MAX)
|
|
398
|
-
.optional(),
|
|
399
|
-
worker_run_ids: z.array(stableIdSchema).max(DEEP_RESEARCH_STEP_LIST_ITEMS_MAX).optional(),
|
|
400
|
-
blocked_reason: boundedText.optional(),
|
|
401
|
-
})
|
|
402
|
-
.superRefine((input, ctx) => {
|
|
403
|
-
if (input.kind === 'local_exploration' && (input.roots?.length ?? 0) === 0) {
|
|
404
|
-
ctx.addIssue({
|
|
405
|
-
code: z.ZodIssueCode.custom,
|
|
406
|
-
path: ['roots'],
|
|
407
|
-
message: 'Local exploration requires at least one bounded root.',
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
if (input.kind === 'web_research' && (input.keywords?.length ?? 0) === 0) {
|
|
411
|
-
ctx.addIssue({
|
|
412
|
-
code: z.ZodIssueCode.custom,
|
|
413
|
-
path: ['keywords'],
|
|
414
|
-
message: 'Web research requires at least one query or keyword.',
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
if (input.status === 'completed' && (input.evidence_artifact_ids?.length ?? 0) === 0) {
|
|
418
|
-
ctx.addIssue({
|
|
419
|
-
code: z.ZodIssueCode.custom,
|
|
420
|
-
path: ['evidence_artifact_ids'],
|
|
421
|
-
message: 'Completed research steps require persisted evidence.',
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
if (input.status === 'blocked' && !input.blocked_reason) {
|
|
425
|
-
ctx.addIssue({
|
|
426
|
-
code: z.ZodIssueCode.custom,
|
|
427
|
-
path: ['blocked_reason'],
|
|
428
|
-
message: 'Blocked research steps require a reason.',
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
if (input.status !== 'blocked' && input.blocked_reason) {
|
|
432
|
-
ctx.addIssue({
|
|
433
|
-
code: z.ZodIssueCode.custom,
|
|
434
|
-
path: ['blocked_reason'],
|
|
435
|
-
message: 'A blocked reason is only valid for blocked research steps.',
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
}),
|
|
439
|
-
impl: async (input, ctx) => {
|
|
440
|
-
await requireRun(deps.store, ctx.sessionId);
|
|
441
|
-
const run = await deps.store.recordStep(ctx.sessionId, {
|
|
442
|
-
kind: input.kind,
|
|
443
|
-
status: input.status,
|
|
444
|
-
objective: input.objective,
|
|
445
|
-
summary: input.summary,
|
|
446
|
-
roots: dedupe(input.roots ?? []),
|
|
447
|
-
keywords: dedupe(input.keywords ?? []),
|
|
448
|
-
ignoredPaths: dedupe(input.ignored_paths ?? []),
|
|
449
|
-
stoppingCondition: input.stopping_condition,
|
|
450
|
-
expectedEvidence: input.expected_evidence,
|
|
451
|
-
evidenceArtifactIds: dedupe(input.evidence_artifact_ids ?? []),
|
|
452
|
-
inspectedRefs: (input.inspected_refs ?? []).map((ref) => ({
|
|
453
|
-
kind: ref.kind,
|
|
454
|
-
locator: ref.locator,
|
|
455
|
-
...(ref.label ? { label: ref.label } : {}),
|
|
456
|
-
...(ref.source_artifact_id ? { sourceArtifactId: ref.source_artifact_id } : {}),
|
|
457
|
-
})),
|
|
458
|
-
workerRunIds: dedupe(input.worker_run_ids ?? []),
|
|
459
|
-
...(input.blocked_reason ? { blockedReason: input.blocked_reason } : {}),
|
|
460
|
-
}, mutationContext(ctx));
|
|
461
|
-
return renderRunStatus(run);
|
|
462
|
-
},
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
function buildCheckpointTool(deps) {
|
|
466
|
-
const itemArray = z
|
|
467
|
-
.array(z.string().trim().min(1).max(DEEP_RESEARCH_CHECKPOINT_ITEM_MAX_CHARS))
|
|
468
|
-
.max(DEEP_RESEARCH_CHECKPOINT_ITEMS_MAX);
|
|
469
|
-
const refArray = z.array(stableIdSchema).max(DEEP_RESEARCH_REFS_MAX);
|
|
470
|
-
return {
|
|
471
|
-
name: DEEP_RESEARCH_CHECKPOINT_TOOL_NAME,
|
|
472
|
-
displayName: 'Checkpoint Research',
|
|
473
|
-
description: 'Record a durable research checkpoint after a meaningful round or before context compaction. ' +
|
|
474
|
-
'Include unresolved questions, next steps, and the artifacts needed to resume.',
|
|
475
|
-
parameters: z.object({
|
|
476
|
-
round: z.number().int().min(1).describe('Monotonic research round number.'),
|
|
477
|
-
stage: z.enum(DEEP_RESEARCH_ACTIVE_STAGES).describe('Current two-stage workflow phase.'),
|
|
478
|
-
status: z
|
|
479
|
-
.enum(['active', 'blocked'])
|
|
480
|
-
.describe('Whether research can proceed without outside input.'),
|
|
481
|
-
summary: z
|
|
482
|
-
.string()
|
|
483
|
-
.trim()
|
|
484
|
-
.min(1)
|
|
485
|
-
.max(DEEP_RESEARCH_CHECKPOINT_TEXT_MAX_CHARS)
|
|
486
|
-
.describe('What was established during this round.'),
|
|
487
|
-
open_questions: itemArray
|
|
488
|
-
.optional()
|
|
489
|
-
.describe('Questions still requiring evidence or resolution.'),
|
|
490
|
-
next_steps: itemArray.optional().describe('Concrete continuation steps.'),
|
|
491
|
-
artifact_ids: refArray.optional().describe('Known research artifact ids required to resume.'),
|
|
492
|
-
}),
|
|
493
|
-
impl: async (input, ctx) => {
|
|
494
|
-
await requireRun(deps.store, ctx.sessionId);
|
|
495
|
-
const run = await deps.store.recordCheckpoint(ctx.sessionId, {
|
|
496
|
-
round: input.round,
|
|
497
|
-
stage: input.stage,
|
|
498
|
-
status: input.status,
|
|
499
|
-
summary: input.summary,
|
|
500
|
-
openQuestions: dedupe(input.open_questions ?? []),
|
|
501
|
-
nextSteps: dedupe(input.next_steps ?? []),
|
|
502
|
-
taskIds: [],
|
|
503
|
-
artifactIds: dedupe(input.artifact_ids ?? []),
|
|
504
|
-
}, mutationContext(ctx));
|
|
505
|
-
return renderRunStatus(run);
|
|
506
|
-
},
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
function buildStatusTool(deps) {
|
|
510
|
-
return {
|
|
511
|
-
name: DEEP_RESEARCH_STATUS_TOOL_NAME,
|
|
512
|
-
displayName: 'Read Research Workspace',
|
|
513
|
-
description: 'Read the durable Deep Research workspace projection. Use after interruption, context compaction, ' +
|
|
514
|
-
'or process restart to recover the objective, stage, latest checkpoint, and artifact inventory.',
|
|
515
|
-
parameters: z.object({}),
|
|
516
|
-
impl: async (_input, ctx) => {
|
|
517
|
-
const run = await deps.store.read(ctx.sessionId);
|
|
518
|
-
return run ? renderRunStatus(run) : '<deep-research-workspace state="uninitialized" />';
|
|
519
|
-
},
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
function buildCompleteTool(deps) {
|
|
523
|
-
const handoffList = z
|
|
524
|
-
.array(z.string().trim().min(1).max(DEEP_RESEARCH_CHECKPOINT_ITEM_MAX_CHARS))
|
|
525
|
-
.max(DEEP_RESEARCH_CHECKPOINT_ITEMS_MAX);
|
|
526
|
-
return {
|
|
527
|
-
name: DEEP_RESEARCH_COMPLETE_TOOL_NAME,
|
|
528
|
-
displayName: 'Complete Research',
|
|
529
|
-
description: 'Complete Deep Research only after every checklist item and required report section is settled. ' +
|
|
530
|
-
'A saved handoff artifact and structured implementation, issue/PR, and verification guidance are required.',
|
|
531
|
-
parameters: z
|
|
532
|
-
.object({
|
|
533
|
-
report_artifact_id: stableIdSchema.describe('Artifact id of the final source-backed report.'),
|
|
534
|
-
handoff_artifact_id: stableIdSchema.describe('Artifact id of the saved role=handoff artifact.'),
|
|
535
|
-
implementation_tasks: handoffList.min(1),
|
|
536
|
-
recommended_issues: handoffList.optional(),
|
|
537
|
-
recommended_pull_requests: handoffList.optional(),
|
|
538
|
-
verification_commands: handoffList.min(1),
|
|
539
|
-
})
|
|
540
|
-
.superRefine((input, ctx) => {
|
|
541
|
-
if ((input.recommended_issues?.length ?? 0) === 0 &&
|
|
542
|
-
(input.recommended_pull_requests?.length ?? 0) === 0) {
|
|
543
|
-
ctx.addIssue({
|
|
544
|
-
code: z.ZodIssueCode.custom,
|
|
545
|
-
path: ['recommended_issues'],
|
|
546
|
-
message: 'Provide at least one recommended issue or pull request.',
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
}),
|
|
550
|
-
impl: async (input, ctx) => {
|
|
551
|
-
const handoff = {
|
|
552
|
-
artifactId: input.handoff_artifact_id,
|
|
553
|
-
implementationTasks: dedupe(input.implementation_tasks),
|
|
554
|
-
recommendedIssues: dedupe(input.recommended_issues ?? []),
|
|
555
|
-
recommendedPullRequests: dedupe(input.recommended_pull_requests ?? []),
|
|
556
|
-
verificationCommands: dedupe(input.verification_commands),
|
|
557
|
-
};
|
|
558
|
-
const replay = await findToolCallEvent(deps.store, ctx.sessionId, ctx.toolCallId);
|
|
559
|
-
if (replay) {
|
|
560
|
-
const run = await deps.store.complete(ctx.sessionId, input.report_artifact_id, handoff, mutationContext(ctx));
|
|
561
|
-
return renderRunStatus(run);
|
|
562
|
-
}
|
|
563
|
-
const existing = await requireActiveRun(deps.store, ctx.sessionId);
|
|
564
|
-
await validateCompletionArtifacts(deps.artifactStore, existing, input.report_artifact_id, input.handoff_artifact_id);
|
|
565
|
-
const run = await deps.store.complete(ctx.sessionId, input.report_artifact_id, handoff, mutationContext(ctx));
|
|
566
|
-
return renderRunStatus(run);
|
|
567
|
-
},
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
async function requireActiveRun(store, sessionId) {
|
|
571
|
-
const run = await store.read(sessionId);
|
|
572
|
-
if (!run) {
|
|
573
|
-
throw new Error(`Call ${DEEP_RESEARCH_START_TOOL_NAME} before using the research workspace`);
|
|
574
|
-
}
|
|
575
|
-
if (run.status === 'completed') {
|
|
576
|
-
throw new Error('Deep Research workspace is already completed');
|
|
577
|
-
}
|
|
578
|
-
return run;
|
|
579
|
-
}
|
|
580
|
-
async function requireRun(store, sessionId) {
|
|
581
|
-
const run = await store.read(sessionId);
|
|
582
|
-
if (!run) {
|
|
583
|
-
throw new Error(`Call ${DEEP_RESEARCH_START_TOOL_NAME} before using the research workspace`);
|
|
584
|
-
}
|
|
585
|
-
return run;
|
|
586
|
-
}
|
|
587
|
-
async function findToolCallEvent(store, sessionId, toolCallId) {
|
|
588
|
-
return (await store.readEvents(sessionId)).find((event) => event.refs?.toolCallId === toolCallId);
|
|
589
|
-
}
|
|
590
|
-
async function validateCompletionArtifacts(artifactStore, run, reportArtifactId, handoffArtifactId) {
|
|
591
|
-
const required = new Map();
|
|
592
|
-
for (const source of run.artifacts.filter((artifact) => artifact.role === 'source')) {
|
|
593
|
-
required.set(source.artifactId, source);
|
|
594
|
-
}
|
|
595
|
-
for (const section of run.reportSections) {
|
|
596
|
-
if (!section.artifactId) {
|
|
597
|
-
throw new Error(`Deep Research report section ${section.key} has no current artifact`);
|
|
598
|
-
}
|
|
599
|
-
const ref = run.artifacts.find((artifact) => artifact.artifactId === section.artifactId);
|
|
600
|
-
if (!ref || ref.role !== 'report_section' || ref.reportSectionKey !== section.key) {
|
|
601
|
-
throw new Error(`Deep Research report section ${section.key} has an invalid current artifact`);
|
|
602
|
-
}
|
|
603
|
-
required.set(ref.artifactId, ref);
|
|
604
|
-
}
|
|
605
|
-
for (const [artifactId, role] of [
|
|
606
|
-
[reportArtifactId, 'report'],
|
|
607
|
-
[handoffArtifactId, 'handoff'],
|
|
608
|
-
]) {
|
|
609
|
-
const ref = run.artifacts.find((artifact) => artifact.artifactId === artifactId);
|
|
610
|
-
if (!ref || ref.role !== role) {
|
|
611
|
-
throw new Error(`Deep Research ${role} artifact ${artifactId} is missing from the ledger`);
|
|
612
|
-
}
|
|
613
|
-
required.set(ref.artifactId, ref);
|
|
614
|
-
}
|
|
615
|
-
for (const ref of required.values()) {
|
|
616
|
-
await validateArtifactIntegrity(artifactStore, run.sessionId, ref);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
async function validateArtifactIntegrity(artifactStore, sessionId, ref) {
|
|
620
|
-
const record = await artifactStore.get(ref.artifactId);
|
|
621
|
-
if (!record) {
|
|
622
|
-
throw new Error(`Deep Research artifact ${ref.artifactId} is missing or deleted`);
|
|
623
|
-
}
|
|
624
|
-
if (record.sessionId !== sessionId || record.source !== 'deep_research') {
|
|
625
|
-
throw new Error(`Deep Research artifact ${ref.artifactId} belongs to another workspace`);
|
|
626
|
-
}
|
|
627
|
-
if (record.kind !== 'file' ||
|
|
628
|
-
record.mimeType !== 'text/markdown' ||
|
|
629
|
-
record.deepResearchRole !== ref.role) {
|
|
630
|
-
throw new Error(`Deep Research artifact ${ref.artifactId} type or role does not match the ledger`);
|
|
631
|
-
}
|
|
632
|
-
const read = await artifactStore.readText(ref.artifactId, {
|
|
633
|
-
maxBytes: DEEP_RESEARCH_ARTIFACT_CONTENT_MAX_CHARS * 4,
|
|
634
|
-
});
|
|
635
|
-
if (!read.ok) {
|
|
636
|
-
throw new Error(`Deep Research artifact ${ref.artifactId} could not be read: ${read.reason}`);
|
|
637
|
-
}
|
|
638
|
-
const contentHash = `sha256:${createHash('sha256').update(read.text).digest('hex')}`;
|
|
639
|
-
if (contentHash !== ref.contentHash) {
|
|
640
|
-
throw new Error(`Deep Research artifact ${ref.artifactId} content does not match the ledger`);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
function mutationContext(ctx) {
|
|
644
|
-
return {
|
|
645
|
-
...(ctx.runId ? { runId: ctx.runId } : {}),
|
|
646
|
-
turnId: ctx.turnId,
|
|
647
|
-
toolCallId: ctx.toolCallId,
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
function stableArtifactId(ctx) {
|
|
651
|
-
const digest = createHash('sha256')
|
|
652
|
-
.update(`${ctx.sessionId}\n${ctx.turnId}\n${ctx.toolCallId}`)
|
|
653
|
-
.digest('hex');
|
|
654
|
-
const uuidLike = [
|
|
655
|
-
digest.slice(0, 8),
|
|
656
|
-
digest.slice(8, 12),
|
|
657
|
-
digest.slice(12, 16),
|
|
658
|
-
digest.slice(16, 20),
|
|
659
|
-
digest.slice(20, 32),
|
|
660
|
-
].join('-');
|
|
661
|
-
return `dr-${uuidLike}`;
|
|
662
|
-
}
|
|
663
|
-
function dedupe(values) {
|
|
664
|
-
return [...new Set(values)];
|
|
665
|
-
}
|
|
666
|
-
function sameStringArray(left, right) {
|
|
667
|
-
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
668
|
-
}
|
|
669
|
-
function normalizeInlineText(value) {
|
|
670
|
-
return redactSecrets(value)
|
|
671
|
-
.replace(/<\/?deep-research-(?:workspace|artifact)\b[^>]{0,4096}>/gi, '')
|
|
672
|
-
.replace(/\s+/g, ' ')
|
|
673
|
-
.trim();
|
|
674
|
-
}
|
|
675
|
-
function safeResearchArtifactContent(value) {
|
|
676
|
-
return redactSecrets(value).replace(/<\/?deep-research-(?:workspace|artifact)\b[^>]{0,4096}>/gi, '');
|
|
677
|
-
}
|
|
678
|
-
export function renderDeepResearchRunStatus(run) {
|
|
679
|
-
const latest = run.checkpoints.at(-1);
|
|
680
|
-
const lines = [
|
|
681
|
-
`<deep-research-workspace status="${run.status}" stage="${run.stage}" scope="${run.scopeLevel}" round="${run.round}">`,
|
|
682
|
-
`Objective: ${normalizeInlineText(run.objective)}`,
|
|
683
|
-
`Artifacts: ${run.artifacts.length} (${DEEP_RESEARCH_ARTIFACT_ROLES.map((role) => `${role}=${run.artifacts.filter((artifact) => artifact.role === role).length}`).join(', ')})`,
|
|
684
|
-
];
|
|
685
|
-
const visibleArtifacts = run.artifacts.slice(-DEEP_RESEARCH_STATUS_ARTIFACTS_MAX);
|
|
686
|
-
for (const artifact of visibleArtifacts) {
|
|
687
|
-
lines.push(`- ${normalizeInlineText(artifact.artifactId)} [${artifact.role}] ${normalizeInlineText(artifact.name)}`);
|
|
688
|
-
}
|
|
689
|
-
lines.push('Checklist:');
|
|
690
|
-
for (const item of run.checklist) {
|
|
691
|
-
lines.push(`- [${item.status}] ${normalizeInlineText(item.itemId)}: ${normalizeInlineText(item.title)}` +
|
|
692
|
-
(item.evidenceArtifactIds.length > 0
|
|
693
|
-
? ` (evidence: ${item.evidenceArtifactIds.join(', ')})`
|
|
694
|
-
: '') +
|
|
695
|
-
(item.blockedReason ? ` (blocked: ${normalizeInlineText(item.blockedReason)})` : ''));
|
|
696
|
-
}
|
|
697
|
-
lines.push('Report sections:');
|
|
698
|
-
for (const section of run.reportSections) {
|
|
699
|
-
lines.push(`- [${section.status}] ${section.key}${section.artifactId ? ` (${section.artifactId})` : ''}`);
|
|
700
|
-
}
|
|
701
|
-
lines.push(`Research steps: ${run.steps.length}`);
|
|
702
|
-
for (const step of run.steps.slice(-10)) {
|
|
703
|
-
lines.push(`- [${step.status}] ${step.kind}: ${normalizeInlineText(step.summary)}` +
|
|
704
|
-
(step.workerRunIds.length > 0 ? ` (workers: ${step.workerRunIds.join(', ')})` : '') +
|
|
705
|
-
(step.blockedReason ? ` (blocked: ${normalizeInlineText(step.blockedReason)})` : ''));
|
|
706
|
-
}
|
|
707
|
-
if (run.artifacts.length > visibleArtifacts.length) {
|
|
708
|
-
lines.push(`- ${run.artifacts.length - visibleArtifacts.length} older artifact(s) omitted from this status view`);
|
|
709
|
-
}
|
|
710
|
-
if (latest) {
|
|
711
|
-
lines.push(`Latest checkpoint: ${normalizeInlineText(latest.summary)}`);
|
|
712
|
-
if (latest.openQuestions.length > 0) {
|
|
713
|
-
lines.push(`Open questions: ${latest.openQuestions.map(normalizeInlineText).join(' | ')}`);
|
|
714
|
-
}
|
|
715
|
-
if (latest.nextSteps.length > 0) {
|
|
716
|
-
lines.push(`Next steps: ${latest.nextSteps.map(normalizeInlineText).join(' | ')}`);
|
|
717
|
-
}
|
|
718
|
-
if (latest.artifactIds.length > 0) {
|
|
719
|
-
lines.push(`Resume artifacts: ${latest.artifactIds.join(', ')}`);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
if (run.reportArtifactId)
|
|
723
|
-
lines.push(`Final report: ${run.reportArtifactId}`);
|
|
724
|
-
if (run.handoff) {
|
|
725
|
-
lines.push(`Handoff artifact: ${run.handoff.artifactId}`);
|
|
726
|
-
lines.push(`Implementation tasks: ${run.handoff.implementationTasks.map(normalizeInlineText).join(' | ')}`);
|
|
727
|
-
lines.push(`Verification commands: ${run.handoff.verificationCommands.map(normalizeInlineText).join(' | ')}`);
|
|
728
|
-
}
|
|
729
|
-
lines.push('</deep-research-workspace>');
|
|
730
|
-
return lines.join('\n');
|
|
731
|
-
}
|
|
732
|
-
const renderRunStatus = renderDeepResearchRunStatus;
|