picocode-core 0.9.163 → 0.9.164
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 +2 -1
- package/src/attachments.js +9 -0
- package/src/controller.js +9 -0
- package/src/derive.js +1 -1
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises'
|
|
|
2
2
|
import { execFile } from 'node:child_process'
|
|
3
3
|
import { promisify } from 'node:util'
|
|
4
4
|
import { compose, scope, model, noToolsCalled, Inherit, getText } from '@prsm/ai'
|
|
5
|
-
import { commitLabel, fileLabel, mediaTypeFor, selectionLabel } from './attachments.js'
|
|
5
|
+
import { commitLabel, elementLabel, fileLabel, mediaTypeFor, selectionLabel } from './attachments.js'
|
|
6
6
|
|
|
7
7
|
const exec = promisify(execFile)
|
|
8
8
|
// a whole patch rides along only while it is small; a bigger commit
|
|
@@ -37,6 +37,7 @@ async function hydratePart(part) {
|
|
|
37
37
|
if (part.type === 'file') return { type: 'text', text: fileLabel(part.path) }
|
|
38
38
|
if (part.type === 'selection') return { type: 'text', text: selectionLabel(part) }
|
|
39
39
|
if (part.type === 'commit') return hydrateCommit(part)
|
|
40
|
+
if (part.type === 'element') return { type: 'text', text: elementLabel(part) }
|
|
40
41
|
if (part.type !== 'image' || part.source?.kind !== 'path') return part
|
|
41
42
|
const mediaType = part.source.mediaType || mediaTypeFor(part.source.path)
|
|
42
43
|
if (!mediaType) return { type: 'text', text: `[image unavailable: ${part.source.path}]` }
|
package/src/attachments.js
CHANGED
|
@@ -73,6 +73,9 @@ export function splitTextByImagePaths(text, exists = existsSync) {
|
|
|
73
73
|
export const fileLabel = (path) => `[file: ${path}]`
|
|
74
74
|
export const selectionLabel = (part) => `[selection: ${part.path}:${part.fromLine}-${part.toLine}]\n${part.text}\n[/selection]`
|
|
75
75
|
export const commitLabel = (part) => `[commit: ${part.hash.slice(0, 7)} ${JSON.stringify(part.subject ?? '')}]`
|
|
76
|
+
// a page element the user picked in a browser: where it is, what it is,
|
|
77
|
+
// and its markup, so the model can find it in the source
|
|
78
|
+
export const elementLabel = (part) => `[element: <${part.tag}> ${part.selector} on ${part.url}${part.component ? ` in component ${part.component}` : ''}]\n${part.html}\n[/element]`
|
|
76
79
|
|
|
77
80
|
// [Image #n] and [File #n] placeholders in the composed text stand for
|
|
78
81
|
// attachments; they become image, file, or selection parts of the user message
|
|
@@ -89,6 +92,8 @@ export function buildUserContent(text, attachments) {
|
|
|
89
92
|
? { type: 'selection', path: attachment.path, text: attachment.text, fromLine: attachment.fromLine, toLine: attachment.toLine, ...(Number.isInteger(attachment.fromColumn) ? { fromColumn: attachment.fromColumn } : {}), ...(Number.isInteger(attachment.toColumn) ? { toColumn: attachment.toColumn } : {}) }
|
|
90
93
|
: attachment.kind === 'commit'
|
|
91
94
|
? { type: 'commit', hash: attachment.hash, subject: attachment.subject, root: attachment.root }
|
|
95
|
+
: attachment.kind === 'element'
|
|
96
|
+
? { type: 'element', url: attachment.url, selector: attachment.selector, tag: attachment.tag, text: attachment.text, html: attachment.html, rect: attachment.rect, component: attachment.component ?? null }
|
|
92
97
|
: attachment.kind === 'file'
|
|
93
98
|
? { type: 'file', path: attachment.path }
|
|
94
99
|
: { type: 'image', source: { kind: 'path', path: attachment.path, mediaType: attachment.mediaType } })
|
|
@@ -131,6 +136,10 @@ export function inputTextFromContent(content, { attachments, nextId }) {
|
|
|
131
136
|
const placeholder = `[File #${nextId()}]`
|
|
132
137
|
attachments.set(placeholder, { path: part.path, kind: 'file' })
|
|
133
138
|
out += placeholder
|
|
139
|
+
} else if (part.type === 'element' && part.selector) {
|
|
140
|
+
const placeholder = `[File #${nextId()}]`
|
|
141
|
+
attachments.set(placeholder, { url: part.url, selector: part.selector, tag: part.tag, text: part.text, html: part.html, rect: part.rect, component: part.component ?? null, kind: 'element' })
|
|
142
|
+
out += placeholder
|
|
134
143
|
} else if (part.type === 'commit' && part.hash) {
|
|
135
144
|
const placeholder = `[File #${nextId()}]`
|
|
136
145
|
attachments.set(placeholder, { hash: part.hash, subject: part.subject, root: part.root, kind: 'commit' })
|
package/src/controller.js
CHANGED
|
@@ -899,6 +899,14 @@ export function createController({ boot }) {
|
|
|
899
899
|
return placeholder
|
|
900
900
|
}
|
|
901
901
|
|
|
902
|
+
// an element picked in a browser page; the message carries its markup
|
|
903
|
+
function attachElement({ url, selector, tag, text = '', html = '', rect = null, component = null }) {
|
|
904
|
+
if (!selector || !tag) return null
|
|
905
|
+
const placeholder = `[File #${++state.imageCount}]`
|
|
906
|
+
state.attachments.set(placeholder, { url: String(url ?? ''), selector: String(selector), tag: String(tag), text: String(text).slice(0, 400), html: String(html).slice(0, 4000), rect, component: component ? String(component) : null, kind: 'element' })
|
|
907
|
+
return placeholder
|
|
908
|
+
}
|
|
909
|
+
|
|
902
910
|
// a project-relative pick: images attach as images, anything else as a
|
|
903
911
|
// file reference
|
|
904
912
|
function attachProjectFile(file) {
|
|
@@ -1398,6 +1406,7 @@ export function createController({ boot }) {
|
|
|
1398
1406
|
attachFile,
|
|
1399
1407
|
attachSelection,
|
|
1400
1408
|
attachCommit,
|
|
1409
|
+
attachElement,
|
|
1401
1410
|
viewImage: deliverImage,
|
|
1402
1411
|
attachProjectFile,
|
|
1403
1412
|
detachImage,
|
package/src/derive.js
CHANGED
|
@@ -101,7 +101,7 @@ function foldMessage(state, event) {
|
|
|
101
101
|
if (message.role === 'user') {
|
|
102
102
|
const text = Array.isArray(message.content)
|
|
103
103
|
? message.content
|
|
104
|
-
.map((p) => (p.type === 'text' ? p.text : p.type === 'file' ? `[file: ${p.path}]` : p.type === 'selection' ? `[selection: ${p.path}:${p.fromLine}-${p.toLine}]` : p.type === 'commit' ? `[commit: ${String(p.hash).slice(0, 7)}]` : `[image: ${String(p.source?.path || '').split('/').pop() || 'attached'}]`))
|
|
104
|
+
.map((p) => (p.type === 'text' ? p.text : p.type === 'file' ? `[file: ${p.path}]` : p.type === 'selection' ? `[selection: ${p.path}:${p.fromLine}-${p.toLine}]` : p.type === 'commit' ? `[commit: ${String(p.hash).slice(0, 7)}]` : p.type === 'element' ? `[element: ${p.selector}]` : `[image: ${String(p.source?.path || '').split('/').pop() || 'attached'}]`))
|
|
105
105
|
.join('')
|
|
106
106
|
: String(message.content)
|
|
107
107
|
state.transcript.push({ ...base, kind: 'user', text, content: message.content, ...(event.data.origin ? { origin: event.data.origin } : {}) })
|