@davait/aipictury 1.9.2 → 1.10.3

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/ext/agent.js CHANGED
@@ -1046,7 +1046,10 @@ async function runHelper(args, { server, session, signal, report, hooks, model }
1046
1046
  const allowedTools = role?.tools.length ? new Set(role.tools.flatMap((t) => AGENT_TOOLS[t] || [t])) : null
1047
1047
  const writes = !allowedTools || [...allowedTools].some((t) => WRITES.has(t) || t === 'run_command')
1048
1048
  const type = role ? (writes ? 'general' : 'explore') : args.subagent_type === 'general' ? 'general' : 'explore'
1049
- const sub = { allowAll: session.allowAll, allowCommands: session.allowCommands, allowSites: session.allowSites, seen: new Map(), cwd: session.cwd, worktree: session.worktree }
1049
+ // The run's own rules hold for its helpers too: a tool denied for the run (--disallowedTools)
1050
+ // was still offered to a helper, which ran the command the run could not.
1051
+ const sub = { allowAll: session.allowAll, allowCommands: session.allowCommands, allowSites: session.allowSites, seen: new Map(), cwd: session.cwd, worktree: session.worktree,
1052
+ ...(session.commandAllow ? { commandAllow: session.commandAllow } : {}), ...(session.commandDeny ? { commandDeny: session.commandDeny } : {}) }
1050
1053
  // isolation: "worktree" — the helper works in a checkout of its own.
1051
1054
  let ownTree = null
1052
1055
  if (args.isolation === 'worktree') {
@@ -1055,7 +1058,9 @@ async function runHelper(args, { server, session, signal, report, hooks, model }
1055
1058
  sub.cwd = ownTree.path
1056
1059
  }
1057
1060
  const readOnly = type === 'explore' || modeNow() === 'plan'
1061
+ const denied = new Set((session.commandDeny || []).flatMap((rule) => permissions.toolsNamed(rule)))
1058
1062
  const tools = toolSchema({ readOnly, helper: true, extra: [...mcp.schema({ readOnly, loaded: (session.loadedMcp ||= new Set()) }), ...mcp.resourceSchema()] })
1063
+ .filter((t) => !denied.has(t.function?.name))
1059
1064
  .filter((t) => !allowedTools || allowedTools.has(t.function.name) || (t.function.name.startsWith('mcp__') && allowedTools.has(t.function.name)))
1060
1065
  const id = `agent_${helpersOf(session).size + 1}`
1061
1066
  const h = {
package/ext/extension.js CHANGED
@@ -2295,6 +2295,9 @@ function moveConversationsToDisk(context) {
2295
2295
  }
2296
2296
 
2297
2297
  function activate(context) {
2298
+ // This window names itself on every call to its GPU server (gpuRun.js).
2299
+ gpuRun.setApp({ client: 'vscode', server: serverUrl })
2300
+ gpuRun.nameCalls()
2298
2301
  try { moveConversationsToDisk(context) } catch { /* tried again next start */ }
2299
2302
  syncCommandEnv().catch(() => { /* again before each run */ })
2300
2303
  undoOldThinkingSwitch(context).catch(() => { /* the setting stays as it was */ })
package/ext/gpuRun.js CHANGED
@@ -10,11 +10,20 @@ const { AsyncLocalStorage } = require('async_hooks')
10
10
  const store = new AsyncLocalStorage()
11
11
  const BEAT = 30_000
12
12
 
13
- /** The headers of the run this code is part of (none outside a run). */
13
+ // This app (this VS Code window, this CLI): on every call to its server, so the server lets its
14
+ // quick calls (a title, a rule saved) and its own runs share the GPU — never another app's.
15
+ const app = { id: `app-${crypto.randomUUID()}`, client: 'vscode', server: () => '' }
16
+ /** Who this app is ('vscode' | 'cli') and where its GPU server is (a function: it can change). */
17
+ function setApp({ client, server } = {}) { if (client) app.client = client; if (server) app.server = server }
18
+
19
+ /** The headers of every call this code makes to its server: the app's, and the run's in a run. */
14
20
  function headers() {
21
+ const own = { 'X-AIPictury-App': app.id, 'X-AIPictury-Client': app.client }
15
22
  const r = store.getStore()
16
- if (!r) return {}
17
- return { 'X-AIPictury-Client': r.client, 'X-AIPictury-Run': r.run, 'X-AIPictury-Label': encodeURIComponent(r.label || '') }
23
+ // Work a run left going after it ended (a rule saved, a title written) is no longer that run:
24
+ // named so, it took the GPU back for a run that was over, and other apps waited 90 s.
25
+ if (!r || r.ended) return own
26
+ return { ...own, 'X-AIPictury-Client': r.client, 'X-AIPictury-Run': r.run, 'X-AIPictury-Label': encodeURIComponent(r.label || '') }
18
27
  }
19
28
 
20
29
  class GpuBusyError extends Error {
@@ -30,8 +39,29 @@ class GpuBusyError extends Error {
30
39
  * fn() as a run holding the GPU of `server`. `client`: 'vscode' | 'cli'. A GPU held by another
31
40
  * run: GpuBusyError before anything is done. A server without holds (older): fn() as before.
32
41
  */
42
+ // Every call a run makes to its own server carries the run, whoever made the call: not only
43
+ // through the account's fetch (account.js serverFetch), which a window without an account (the
44
+ // extension's tests) does not have — there the run's own model calls were refused as another's.
45
+ function nameCalls() {
46
+ if (globalThis.fetch.namesRuns) return
47
+ const base = globalThis.fetch
48
+ const named = (input, init = {}) => {
49
+ const r = store.getStore()
50
+ const url = typeof input === 'string' ? input : input?.url || String(input)
51
+ const server = (r && !r.ended && r.server) || String(app.server() || '').replace(/\/+$/, '')
52
+ if (!server || !url.startsWith(server)) return base(input, init)
53
+ const h = new Headers(init.headers || (typeof input === 'object' && input?.headers) || undefined)
54
+ for (const [k, v] of Object.entries(headers())) if (!h.has(k)) h.set(k, v)
55
+ return base(input, { ...init, headers: h })
56
+ }
57
+ named.namesRuns = true
58
+ globalThis.fetch = named
59
+ }
60
+
33
61
  async function holding({ server, client, label = '' }, fn) {
34
- const run = { client, run: `${client}-${crypto.randomUUID()}`, label: String(label).replace(/\s+/g, ' ').slice(0, 100) }
62
+ nameCalls()
63
+ const run = { client, server: String(server || '').replace(/\/+$/, ''), run: `${client}-${crypto.randomUUID()}`,
64
+ label: String(label).replace(/\s+/g, ' ').slice(0, 100) }
35
65
  return store.run(run, async () => {
36
66
  const h = headers()
37
67
  let held = false
@@ -53,6 +83,7 @@ async function holding({ server, client, label = '' }, fn) {
53
83
  try {
54
84
  return await fn()
55
85
  } finally {
86
+ run.ended = true
56
87
  if (beat) clearInterval(beat)
57
88
  if (held) await fetch(`${server}/api/gpu/hold`, { method: 'DELETE', headers: h, signal: AbortSignal.timeout(10_000) }).catch(() => {})
58
89
  }
@@ -70,8 +101,9 @@ async function state(server) {
70
101
  /** The hold of another run on every GPU (all busy), or null (one is free, or it is this run's). */
71
102
  function busyFor(gpu, mine = null) {
72
103
  const lanes = gpu?.lanes || []
73
- if (!lanes.length || lanes.some((l) => !l.hold || l.hold.run === mine)) return null
104
+ // A quick call (no run: a title, a rule saved) is done in a moment; the server lets its owner in.
105
+ if (!lanes.length || lanes.some((l) => !l.hold || l.hold.run === mine || l.hold.side)) return null
74
106
  return lanes[0].hold
75
107
  }
76
108
 
77
- module.exports = { holding, headers, state, busyFor, GpuBusyError, store }
109
+ module.exports = { holding, headers, setApp, nameCalls, state, busyFor, GpuBusyError, store }
package/ext/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "aipictury",
3
3
  "displayName": "AIPictury",
4
4
  "description": "A coding agent and a generation studio running on your own GPU, with uncensored local models.",
5
- "version": "1.9.2",
5
+ "version": "1.10.3",
6
6
  "publisher": "aipictury",
7
7
  "license": "SEE LICENSE IN LICENSE.md",
8
8
  "engines": {
@@ -1203,7 +1203,7 @@
1203
1203
  "@eslint/js": "^10.0.1",
1204
1204
  "@types/vscode": "^1.95.0",
1205
1205
  "@vscode/test-electron": "^3.1.0",
1206
- "@vscode/vsce": "^3.2.0",
1206
+ "@vscode/vsce": "^4.0.0",
1207
1207
  "dompurify": "^3.4.16",
1208
1208
  "esbuild": "^0.28.2",
1209
1209
  "eslint": "^10.11.0",
package/ext/storage.js CHANGED
@@ -26,11 +26,12 @@ class Storage {
26
26
  }
27
27
 
28
28
  /** Stores bytes at `path` ("users/u/media/a.png"). Resolves to the object's metadata. A multipart
29
- * upload, as the SDK's: the type goes in the object's metadata, where the rules read it. */
29
+ * upload, as the SDK's: the type goes in the object's metadata, where the rules read it; and
30
+ * no-store, so no browser that shows it keeps a copy past signing out. */
30
31
  async upload(path, bytes, contentType) {
31
32
  const boundary = `aipictury-${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`
32
33
  const body = Buffer.concat([
33
- Buffer.from(`--${boundary}\r\nContent-Type: application/json; charset=utf-8\r\n\r\n${JSON.stringify({ name: path, contentType })}\r\n`
34
+ Buffer.from(`--${boundary}\r\nContent-Type: application/json; charset=utf-8\r\n\r\n${JSON.stringify({ name: path, contentType, cacheControl: 'private, no-store' })}\r\n`
34
35
  + `--${boundary}\r\nContent-Type: ${contentType}\r\n\r\n`),
35
36
  Buffer.from(bytes),
36
37
  Buffer.from(`\r\n--${boundary}--`),
package/main.js CHANGED
@@ -711,6 +711,8 @@ async function main() {
711
711
  if (!require(path.join(EXT, 'gpuServer.js')).explicit()) {
712
712
  try { await require(path.join(EXT, 'gpuServer.js')).adopt((await sync.cloud.getPreferences())?.values?.gpuServer) } catch { /* said below if none */ }
713
713
  }
714
+ // This CLI names itself on every call to its GPU server (gpuRun.js).
715
+ require(path.join(EXT, 'gpuRun.js')).setApp({ client: 'cli', server })
714
716
  // Every call to the GPU server carries the account's token (the server may require it), and only those.
715
717
  const { serverFetch } = require(path.join(EXT, 'account.js'))
716
718
  globalThis.fetch = serverFetch({ fetch: globalThis.fetch, account, server })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davait/aipictury",
3
- "version": "1.9.2",
3
+ "version": "1.10.3",
4
4
  "description": "The AIPictury agent in a terminal: the VS Code extension's agent, on your own GPU server, signed in to your AIPictury account.",
5
5
  "bin": {
6
6
  "aipictury": "main.js"