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 +1 -1
- package/src/agent.js +3 -1
- package/src/attachments.js +12 -5
- package/src/controller.js +12 -2
- package/src/derive.js +1 -1
package/package.json
CHANGED
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}]` }
|
package/src/attachments.js
CHANGED
|
@@ -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: '
|
|
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 (!
|
|
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 })
|