picocode-core 0.9.170 → 0.9.171

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picocode-core",
3
- "version": "0.9.170",
3
+ "version": "0.9.171",
4
4
  "description": "The agent runtime behind pico: sessions, tools, subagents, MCP, memory, and model access",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/agent.js CHANGED
@@ -143,7 +143,7 @@ export async function runTurn({ history, tools, recorder, modelName, effort, aut
143
143
  // a tool may legitimately run for minutes (test suites, slow fetches)
144
144
  // and emits nothing while it does; the watchdog guards the provider
145
145
  // stream, so pause it until the tool finishes. tools carry their own
146
- // timeouts (bash 120s default, web tools via dredge)
146
+ // timeouts (bash 120s default)
147
147
  clearTimeout(watchdog)
148
148
  recorder.currentCall = event.call
149
149
  onStream?.(event)
package/src/controller.js CHANGED
@@ -53,7 +53,7 @@ export const SESSION_COLORS = {
53
53
  gray: '#9ca3af',
54
54
  }
55
55
 
56
- const WORKER_TOOLS = ['read', 'write', 'edit', 'bash', 'glob', 'grep', 'shell_output', 'shell_kill', 'web_search', 'web_fetch']
56
+ const WORKER_TOOLS = ['read', 'write', 'edit', 'bash', 'glob', 'grep', 'shell_output', 'shell_kill']
57
57
  const AGENT_TOOLS = ['agent_plan', 'agent_start', 'agent_list', 'agent_collect', 'agent_cancel']
58
58
  const CONTEXT_COLORS = ['#67b7ff', '#c792ea', '#f7c66a', '#f78c6c', '#6be795']
59
59
 
@@ -202,7 +202,9 @@ export function createController({ boot }) {
202
202
  const sessionId = state.session?.id
203
203
  if (!sessionId) throw new Error('worker requires an active session')
204
204
  const scratchpad = ensureDir(agentScratchDir(boot.root, sessionId, agent.id))
205
- const requestedTools = agent.tools?.length ? agent.tools.filter((name) => WORKER_TOOLS.includes(name)) : WORKER_TOOLS
205
+ const mcpTools = boot.mcp.tools()
206
+ const availableNames = [...WORKER_TOOLS, ...mcpTools.map((tool) => tool.name)]
207
+ const requestedTools = agent.tools?.length ? agent.tools.filter((name) => availableNames.includes(name)) : availableNames
206
208
  const { tools, recorder } = createToolset({
207
209
  cwd: boot.cwd,
208
210
  env: { ...boot.env, PICO_SCRATCHPAD: scratchpad },
@@ -213,10 +215,10 @@ export function createController({ boot }) {
213
215
  shells: boot.shells,
214
216
  sessionId,
215
217
  sessionFile: state.session?.file,
216
- dredge: boot.dredge,
217
218
  signal,
218
219
  maxToolCalls: 30,
219
220
  allowNames: requestedTools,
221
+ mcpTools,
220
222
  })
221
223
  return runTurn({
222
224
  history: [{ role: 'user', content: agent.prompt }],
@@ -277,6 +279,7 @@ export function createController({ boot }) {
277
279
 
278
280
  const runWorker = async ({ history, role, tools: enabled = true, onStream }) => {
279
281
  const scratchpad = ensureDir(agentScratchDir(boot.root, sessionId, `deliberation-${id}-${role}`))
282
+ const mcpTools = enabled ? boot.mcp.tools() : []
280
283
  const toolset = createToolset({
281
284
  cwd: boot.cwd,
282
285
  env: { ...boot.env, PICO_SCRATCHPAD: scratchpad },
@@ -284,10 +287,10 @@ export function createController({ boot }) {
284
287
  shells: boot.shells,
285
288
  sessionId,
286
289
  sessionFile: state.session?.file,
287
- dredge: boot.dredge,
288
290
  signal,
289
291
  maxToolCalls: 30,
290
- allowNames: enabled ? WORKER_TOOLS : [],
292
+ allowNames: enabled ? [...WORKER_TOOLS, ...mcpTools.map((tool) => tool.name)] : [],
293
+ mcpTools,
291
294
  })
292
295
  return runTurn({
293
296
  history,
@@ -622,7 +625,6 @@ export function createController({ boot }) {
622
625
  deliberations: boot.deliberationModel || (boot.proposerModel && boot.reviewerModel) ? deliberations : null,
623
626
  onAgentsCollected: discardCollectedAgentNotes,
624
627
  askUser,
625
- dredge: boot.dredge,
626
628
  mcpTools: boot.mcp.tools(),
627
629
  userTools: userToolScan.tools,
628
630
  signal: controller.signal,
@@ -1216,7 +1218,6 @@ export function createController({ boot }) {
1216
1218
  shells: boot.shells,
1217
1219
  wakeups: boot.wakeups,
1218
1220
  memory: boot.memory,
1219
- dredge: boot.dredge,
1220
1221
  ...extra,
1221
1222
  })
1222
1223
  }
@@ -5,10 +5,9 @@ import { createEdit } from './edit.js'
5
5
  import { createBash } from './bash.js'
6
6
  import { createGlob } from './glob.js'
7
7
  import { createGrep } from './grep.js'
8
- import { createWebTools } from './web.js'
9
8
  import { createView } from './view.js'
10
9
 
11
- export function createToolset({ cwd, env, tracker, skills, shells, sessionId, sessionFile, wakeups, memory, agents, deliberations, onAgentsCollected, askUser, dredge, mcpTools = [], userTools = [], hostTools = [], signal, maxToolCalls, maxAgentStarts, requireAgentPlan = false, allowNames, onToolUpdate, viewer }) {
10
+ export function createToolset({ cwd, env, tracker, skills, shells, sessionId, sessionFile, wakeups, memory, agents, deliberations, onAgentsCollected, askUser, mcpTools = [], userTools = [], hostTools = [], signal, maxToolCalls, maxAgentStarts, requireAgentPlan = false, allowNames, onToolUpdate, viewer }) {
12
11
  const recorder = createRecorder(onToolUpdate)
13
12
  let agentStarts = 0
14
13
  let plannedAgentStarts = requireAgentPlan ? null : maxAgentStarts
@@ -25,10 +24,6 @@ export function createToolset({ cwd, env, tracker, skills, shells, sessionId, se
25
24
 
26
25
  if (viewer) local.push(createView({ ...deps, viewer }))
27
26
 
28
- if (dredge) {
29
- local.push(...createWebTools({ dredge, recorder, signal }))
30
- }
31
-
32
27
  if (shells) {
33
28
  local.push(
34
29
  {
@@ -283,7 +278,7 @@ export function createToolset({ cwd, env, tracker, skills, shells, sessionId, se
283
278
  })
284
279
  }
285
280
 
286
- const describedToolNames = new Set(['read', 'write', 'edit', 'bash', 'glob', 'grep', 'web_search', 'schedule_wakeup'])
281
+ const describedToolNames = new Set(['read', 'write', 'edit', 'bash', 'glob', 'grep', 'schedule_wakeup'])
287
282
  const describedTools = new Set(local.filter((tool) => describedToolNames.has(tool.name)).map((tool) => tool.name))
288
283
  const byName = new Map()
289
284
  for (const tool of [...local, ...hostTools, ...userTools, ...mcpTools]) {
package/src/tools/web.js DELETED
@@ -1,86 +0,0 @@
1
- import { describeParam } from './recorder.js'
2
- const DEFAULT_SLICE_CHARS = 24000
3
- const MAX_SLICE_CHARS = 100000
4
-
5
- export function resolveDredge(config = {}, env = process.env) {
6
- const url = env.DREDGE_URL || config.dredge?.url || null
7
- if (!url) return null
8
- return {
9
- url: url.replace(/\/+$/, ''),
10
- apiKey: env.DREDGE_API_KEY || config.dredge?.apiKey || null,
11
- }
12
- }
13
-
14
- const CALL_TIMEOUT_MS = 120000
15
-
16
- async function call(dredge, path, params, signal) {
17
- const query = new URLSearchParams(params)
18
- // bounded above dredge's own 90s queue ceiling so a wedged server becomes
19
- // a tool error instead of a hung turn
20
- const signals = [AbortSignal.timeout(CALL_TIMEOUT_MS), ...(signal ? [signal] : [])]
21
- const response = await fetch(`${dredge.url}${path}?${query}`, {
22
- signal: AbortSignal.any(signals),
23
- headers: dredge.apiKey ? { authorization: `Bearer ${dredge.apiKey}` } : {},
24
- })
25
- const body = await response.json().catch(() => null)
26
- if (!body) throw new Error(`dredge returned http ${response.status} with no usable body`)
27
- return body
28
- }
29
-
30
- export function createWebTools({ dredge, recorder, signal }) {
31
- return [
32
- {
33
- name: 'web_search',
34
- description:
35
- 'Search the web. Google-style operators work: site:, filetype:pdf, quoted phrases. Returns ranked results; pass a result url to web_fetch to read it.',
36
- schema: {
37
- q: { type: 'string', description: 'the search query' },
38
- description: describeParam,
39
- },
40
- execute: async ({ q }) => {
41
- recorder.extra({ title: q })
42
- const body = await call(dredge, '/search', { q }, signal)
43
- if (!body.ok) throw new Error(body.error?.message || 'search failed')
44
- const results = (body.results || []).map((r) => ({
45
- title: r.title,
46
- url: r.url,
47
- snippet: r.snippet,
48
- source: r.source,
49
- }))
50
- if (results.length === 0) {
51
- const backends = (body.backends || []).map((b) => `${b.name}: ${b.status}`).join(', ')
52
- return { results, note: backends ? `no results · backends: ${backends}` : 'no results' }
53
- }
54
- return { results }
55
- },
56
- },
57
- {
58
- name: 'web_fetch',
59
- description:
60
- 'Fetch a url and read it as clean markdown (html, pdf, docx, and textual formats like json). Long documents arrive in slices: the result says which slice you have (e.g. "slice 1 of 12") and next_cursor continues from there. Every slice you fetch permanently occupies conversation context, so only walk cursors for content you actually need, and raise maxChars only when the task genuinely needs a bigger window.',
61
- schema: {
62
- description: describeParam,
63
- url: { type: 'string', description: 'the url to fetch' },
64
- cursor: { type: 'string', description: 'pagination cursor from a previous web_fetch of the same url', optional: true },
65
- maxChars: { type: 'number', description: `slice size in characters, default ${DEFAULT_SLICE_CHARS}, max ${MAX_SLICE_CHARS}`, optional: true },
66
- },
67
- execute: async ({ url, cursor, maxChars }) => {
68
- recorder.extra({ title: url.replace(/^https?:\/\//, '').slice(0, 80) })
69
- const slice = Math.min(MAX_SLICE_CHARS, Math.max(1000, maxChars || DEFAULT_SLICE_CHARS))
70
- const body = await call(dredge, '/fetch', { url, maxChars: slice, ...(cursor && { cursor }) }, signal)
71
- if (!body.ok) {
72
- const { code, message, retryable } = body.error || {}
73
- throw new Error(`${code || 'fetch failed'}: ${message || url}${retryable ? ' (retryable)' : ''}`)
74
- }
75
- const { markdown, metadata, pagination } = body.doc
76
- return {
77
- markdown,
78
- title: metadata?.title || null,
79
- finalUrl: metadata?.final_url || url,
80
- ...(pagination?.total_chunks > 1 && { slice: `${pagination.chunk_index + 1} of ${pagination.total_chunks}` }),
81
- ...(pagination?.next_cursor && { next_cursor: pagination.next_cursor }),
82
- }
83
- },
84
- },
85
- ]
86
- }