picocode-core 0.9.128 → 0.9.129

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.128",
3
+ "version": "0.9.129",
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
@@ -1,8 +1,10 @@
1
1
  import { readFile } from 'node:fs/promises'
2
2
  import { compose, scope, model, noToolsCalled, Inherit, getText } from '@prsm/ai'
3
- import { mediaTypeFor } from './attachments.js'
3
+ import { fileLabel, mediaTypeFor } from './attachments.js'
4
4
 
5
+ // file parts reach the model as a labelled path; it reads them itself
5
6
  async function hydratePart(part) {
7
+ if (part.type === 'file') return { type: 'text', text: fileLabel(part.path) }
6
8
  if (part.type !== 'image' || part.source?.kind !== 'path') return part
7
9
  const mediaType = part.source.mediaType || mediaTypeFor(part.source.path)
8
10
  if (!mediaType) return { type: 'text', text: `[image unavailable: ${part.source.path}]` }
@@ -68,19 +68,22 @@ export function splitTextByImagePaths(text, exists = existsSync) {
68
68
  return parts
69
69
  }
70
70
 
71
+ export const fileLabel = (path) => `[file: ${path}]`
72
+
73
+ // [Image #n] and [File #n] placeholders in the composed text stand for
74
+ // attachments; they become image and file parts of the user message
71
75
  export function buildUserContent(text, attachments) {
72
76
  const parts = []
73
77
  let last = 0
74
78
  const used = []
75
- for (const match of text.matchAll(/\[Image #\d+\]/g)) {
79
+ for (const match of text.matchAll(/\[(?:Image|File) #\d+\]/g)) {
76
80
  const attachment = attachments.get(match[0])
77
81
  if (!attachment) continue
78
82
  const before = text.slice(last, match.index)
79
83
  if (before) parts.push({ type: 'text', text: before })
80
- parts.push({
81
- type: 'image',
82
- source: { kind: 'path', path: attachment.path, mediaType: attachment.mediaType },
83
- })
84
+ parts.push(attachment.kind === 'file'
85
+ ? { type: 'file', path: attachment.path }
86
+ : { type: 'image', source: { kind: 'path', path: attachment.path, mediaType: attachment.mediaType } })
84
87
  used.push(match[0])
85
88
  last = match.index + match[0].length
86
89
  }
@@ -116,6 +119,10 @@ export function inputTextFromContent(content, { attachments, nextId }) {
116
119
  const placeholder = `[Image #${nextId()}]`
117
120
  attachments.set(placeholder, { path: part.source.path, mediaType: part.source.mediaType || mediaTypeFor(part.source.path) })
118
121
  out += placeholder
122
+ } else if (part.type === 'file' && part.path) {
123
+ const placeholder = `[File #${nextId()}]`
124
+ attachments.set(placeholder, { path: part.path, kind: 'file' })
125
+ out += placeholder
119
126
  } else {
120
127
  out += '[image]'
121
128
  }
package/src/controller.js CHANGED
@@ -807,10 +807,19 @@ export function createController({ boot }) {
807
807
  return placeholder
808
808
  }
809
809
 
810
+ function attachFile(path) {
811
+ if (!existsSync(path)) return null
812
+ const placeholder = `[File #${++state.imageCount}]`
813
+ state.attachments.set(placeholder, { path, kind: 'file' })
814
+ return placeholder
815
+ }
816
+
817
+ // a project-relative pick: images attach as images, anything else as a
818
+ // file reference
810
819
  function attachProjectFile(file) {
811
820
  const full = join(boot.cwd, file)
812
- if (!mediaTypeFor(file) || !existsSync(full)) return null
813
- return attachImage(full)
821
+ if (!existsSync(full)) return null
822
+ return mediaTypeFor(file) ? attachImage(full) : attachFile(full)
814
823
  }
815
824
 
816
825
  function detachImage(placeholder) {
@@ -1246,6 +1255,7 @@ export function createController({ boot }) {
1246
1255
  undoRewind,
1247
1256
  recallText,
1248
1257
  attachImage,
1258
+ attachFile,
1249
1259
  attachProjectFile,
1250
1260
  detachImage,
1251
1261
  costSummary,
package/src/derive.js CHANGED
@@ -100,7 +100,7 @@ function foldMessage(state, event) {
100
100
  if (message.role === 'user') {
101
101
  const text = Array.isArray(message.content)
102
102
  ? message.content
103
- .map((p) => (p.type === 'text' ? p.text : `[image: ${String(p.source?.path || '').split('/').pop() || 'attached'}]`))
103
+ .map((p) => (p.type === 'text' ? p.text : p.type === 'file' ? `[file: ${p.path}]` : `[image: ${String(p.source?.path || '').split('/').pop() || 'attached'}]`))
104
104
  .join('')
105
105
  : String(message.content)
106
106
  state.transcript.push({ ...base, kind: 'user', text, content: message.content })