@shawnstack/quickforge 1.6.12 → 1.7.0

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.
Files changed (46) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/AgentProfilesPage-DQ8URegq.js +1 -0
  3. package/dist/assets/ChatPanelHost-vi1tDqlJ.js +291 -0
  4. package/dist/assets/{PluginsPage-qXTjDrpd.js → PluginsPage-B8bHMwfv.js} +1 -1
  5. package/dist/assets/ScheduledTasksPage-AaUz5el2.js +2 -0
  6. package/dist/assets/{SettingsWorkspacePage-CnFDmGvW.js → SettingsWorkspacePage-BOP9CtoI.js} +112 -105
  7. package/dist/assets/{SharedConversationPage-CkQ-XwYS.js → SharedConversationPage-9A4L9UcQ.js} +1 -1
  8. package/dist/assets/TerminalDock-DQOXpaDe.js +2 -0
  9. package/dist/assets/WorkspaceInspector-DaM7TJHb.js +13 -0
  10. package/dist/assets/icons-ko_i0WpN.js +1 -0
  11. package/dist/assets/index-B2eauKpo.css +3 -0
  12. package/dist/assets/index-B8Awq5FV.js +63 -0
  13. package/dist/assets/{mcp-servers-dialog-CsRQxx7A.js → mcp-servers-dialog-XE4K8PCO.js} +2 -2
  14. package/dist/assets/{monaco-CdOyGlWD.js → monaco-C13M09wD.js} +1 -1
  15. package/dist/assets/{react-vendor-VqdnQHHS.js → react-vendor-2RKYr-A4.js} +1 -1
  16. package/dist/assets/{skills-dialog-CmVLi_h6.js → skills-dialog-MpoxntiV.js} +1 -1
  17. package/dist/index.html +6 -6
  18. package/dist/licenses/material-icon-theme.txt +8 -0
  19. package/package.json +1 -1
  20. package/server/agent-manager.mjs +182 -39
  21. package/server/auto-archive.mjs +137 -0
  22. package/server/auto-compaction.mjs +15 -18
  23. package/server/conversation-compaction.mjs +69 -3
  24. package/server/custom-commands.mjs +17 -0
  25. package/server/image-generation.mjs +130 -0
  26. package/server/index.mjs +12 -1
  27. package/server/provider-config.mjs +78 -0
  28. package/server/routes/agent.mjs +9 -0
  29. package/server/routes/session-assets.mjs +49 -0
  30. package/server/routes/shared-conversation.mjs +10 -0
  31. package/server/routes/storage.mjs +4 -0
  32. package/server/routes/tools.mjs +1 -1
  33. package/server/routes/workspace.mjs +5 -2
  34. package/server/session-assets.mjs +134 -0
  35. package/server/session-persistence-lock.mjs +9 -0
  36. package/server/storage.mjs +24 -1
  37. package/server/tools/definitions.mjs +10 -0
  38. package/server/tools/index.mjs +2 -0
  39. package/dist/assets/AgentProfilesPage-CooGLweI.js +0 -1
  40. package/dist/assets/ChatPanelHost-TEbv0lnL.js +0 -260
  41. package/dist/assets/ScheduledTasksPage-BBor8Yb2.js +0 -2
  42. package/dist/assets/TerminalDock-CvqBBMPg.js +0 -2
  43. package/dist/assets/WorkspaceInspector-6JGc6VGb.js +0 -13
  44. package/dist/assets/icons-C7j5jdKo.js +0 -1
  45. package/dist/assets/index-DxNzlPmc.js +0 -63
  46. package/dist/assets/index-HVxfqXfB.css +0 -3
@@ -0,0 +1,134 @@
1
+ import { promises as fs } from 'node:fs'
2
+ import path from 'node:path'
3
+ import { randomUUID } from 'node:crypto'
4
+ import { storageDir } from './storage.mjs'
5
+
6
+ export const MAX_SESSION_IMAGE_BYTES = 25 * 1024 * 1024
7
+
8
+ const MIME_TYPES = new Map([
9
+ ['image/png', 'png'],
10
+ ['image/jpeg', 'jpg'],
11
+ ['image/webp', 'webp'],
12
+ ['image/gif', 'gif'],
13
+ ])
14
+
15
+ const SAFE_SEGMENT_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/
16
+ const ASSET_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.(?:png|jpg|webp|gif)$/i
17
+
18
+ function requestError(message, statusCode = 400) {
19
+ const error = new Error(message)
20
+ error.statusCode = statusCode
21
+ return error
22
+ }
23
+
24
+ function assertSafeSegment(value, label) {
25
+ if (typeof value !== 'string' || !SAFE_SEGMENT_RE.test(value)) {
26
+ throw requestError(`Invalid ${label}`)
27
+ }
28
+ return value
29
+ }
30
+
31
+ function normalizeBucket(bucket) {
32
+ if (!bucket || typeof bucket !== 'object' || Array.isArray(bucket)) {
33
+ throw requestError('Invalid session asset scope')
34
+ }
35
+ if (bucket.scope === 'global') return { scope: 'global' }
36
+ if (bucket.scope === 'project') {
37
+ return {
38
+ scope: 'project',
39
+ projectId: assertSafeSegment(bucket.projectId, 'projectId'),
40
+ }
41
+ }
42
+ throw requestError('Invalid session asset scope')
43
+ }
44
+
45
+ function assetsRoot(bucket) {
46
+ if (bucket.scope === 'project') {
47
+ return path.join(storageDir, 'conversations', 'projects', bucket.projectId, 'assets')
48
+ }
49
+ return path.join(storageDir, 'conversations', 'global', 'assets')
50
+ }
51
+
52
+ function sessionAssetsDir(bucket, sessionId) {
53
+ return path.join(assetsRoot(normalizeBucket(bucket)), assertSafeSegment(sessionId, 'sessionId'))
54
+ }
55
+
56
+ function normalizeMimeType(mimeType) {
57
+ const value = typeof mimeType === 'string' ? mimeType.trim().toLowerCase() : ''
58
+ const extension = MIME_TYPES.get(value)
59
+ if (!extension) throw requestError(`Unsupported image MIME type: ${mimeType || ''}`)
60
+ return { mimeType: value, extension }
61
+ }
62
+
63
+ function decodeBase64(value) {
64
+ const normalized = typeof value === 'string' ? value.replace(/\s+/g, '') : ''
65
+ if (!normalized || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {
66
+ throw requestError('Invalid base64 image data')
67
+ }
68
+ const buffer = Buffer.from(normalized, 'base64')
69
+ if (buffer.toString('base64') !== normalized) throw requestError('Invalid base64 image data')
70
+ return buffer
71
+ }
72
+
73
+ function normalizeImageData(data) {
74
+ if (Buffer.isBuffer(data)) return data
75
+ if (data instanceof Uint8Array) return Buffer.from(data)
76
+ return decodeBase64(data)
77
+ }
78
+
79
+ function mimeTypeFromAssetId(assetId) {
80
+ const extension = path.extname(assetId).slice(1).toLowerCase()
81
+ for (const [mimeType, candidate] of MIME_TYPES) {
82
+ if (candidate === extension) return mimeType
83
+ }
84
+ throw requestError('Invalid assetId')
85
+ }
86
+
87
+ function assetMetadata(assetId, mimeType, size) {
88
+ return { assetId, mimeType, size }
89
+ }
90
+
91
+ export function validateSessionAssetId(assetId) {
92
+ if (typeof assetId !== 'string' || !ASSET_ID_RE.test(assetId)) {
93
+ throw requestError('Invalid assetId')
94
+ }
95
+ return assetId
96
+ }
97
+
98
+ export async function writeSessionAsset(bucket, sessionId, image) {
99
+ const dir = sessionAssetsDir(bucket, sessionId)
100
+ const { mimeType, extension } = normalizeMimeType(image?.mimeType)
101
+ const data = normalizeImageData(image?.data)
102
+ if (data.byteLength > MAX_SESSION_IMAGE_BYTES) {
103
+ throw requestError(`Image exceeds the ${MAX_SESSION_IMAGE_BYTES} byte limit`, 413)
104
+ }
105
+
106
+ await fs.mkdir(dir, { recursive: true })
107
+ const assetId = `${randomUUID()}.${extension}`
108
+ await fs.writeFile(path.join(dir, assetId), data, { flag: 'wx' })
109
+ return assetMetadata(assetId, mimeType, data.byteLength)
110
+ }
111
+
112
+ export async function readSessionAsset(bucket, sessionId, assetId) {
113
+ const dir = sessionAssetsDir(bucket, sessionId)
114
+ const safeAssetId = validateSessionAssetId(assetId)
115
+ const data = await fs.readFile(path.join(dir, safeAssetId))
116
+ if (data.byteLength > MAX_SESSION_IMAGE_BYTES) {
117
+ throw requestError('Stored image exceeds the allowed size', 413)
118
+ }
119
+ return {
120
+ ...assetMetadata(safeAssetId, mimeTypeFromAssetId(safeAssetId), data.byteLength),
121
+ data,
122
+ }
123
+ }
124
+
125
+ export async function deleteSessionAsset(bucket, sessionId, assetId) {
126
+ const dir = sessionAssetsDir(bucket, sessionId)
127
+ const safeAssetId = validateSessionAssetId(assetId)
128
+ await fs.rm(path.join(dir, safeAssetId), { force: true })
129
+ }
130
+
131
+ export async function deleteSessionAssets(bucket, sessionId) {
132
+ const dir = sessionAssetsDir(bucket, sessionId)
133
+ await fs.rm(dir, { recursive: true, force: true })
134
+ }
@@ -0,0 +1,9 @@
1
+ let persistenceQueue = Promise.resolve()
2
+
3
+ export function withSessionPersistenceLock(operation) {
4
+ const result = persistenceQueue
5
+ .catch(() => undefined)
6
+ .then(operation)
7
+ persistenceQueue = result.then(() => undefined, () => undefined)
8
+ return result
9
+ }
@@ -517,7 +517,12 @@ async function writeSessionValues(data) {
517
517
  existingFiles.map(async (file) => {
518
518
  const sessionId = path.basename(file, '.json')
519
519
  if (!nextIds.has(sessionId)) {
520
+ const bucket = sessionBucketIndex.get(sessionId) || await findSessionBucketByDataFile(sessionId)
520
521
  await fs.rm(file, { force: true })
522
+ if (bucket) {
523
+ const { deleteSessionAssets } = await import('./session-assets.mjs')
524
+ await deleteSessionAssets(bucket, sessionId)
525
+ }
521
526
  sessionBucketIndex.delete(sessionId)
522
527
  }
523
528
  }),
@@ -574,7 +579,12 @@ export async function findSessionBucket(sessionId) {
574
579
  await ensureStorage()
575
580
  await rebuildBucketIndex()
576
581
  }
577
- return sessionBucketIndex.get(sessionId) ?? null
582
+ const indexed = sessionBucketIndex.get(sessionId)
583
+ if (indexed) return indexed
584
+
585
+ const recovered = await findSessionBucketByDataFile(sessionId)
586
+ if (recovered) sessionBucketIndex.set(sessionId, recovered)
587
+ return recovered
578
588
  }
579
589
 
580
590
  export async function readSessionValue(sessionId) {
@@ -594,11 +604,24 @@ export async function writeSessionValue(sessionId, value) {
594
604
  })
595
605
  }
596
606
 
607
+ export async function atomicSessionValueUpdate(sessionId, updateFn) {
608
+ return enqueueWrite('sessions', async () => {
609
+ await ensureStorage()
610
+ const current = await readSessionValue(sessionId)
611
+ if (!current) return null
612
+ const updated = updateFn(current)
613
+ await writeSessionValueFile(sessionId, updated)
614
+ return updated
615
+ })
616
+ }
617
+
597
618
  export async function deleteSessionValue(sessionId) {
598
619
  return enqueueWrite('sessions', async () => {
599
620
  const bucket = await findSessionBucket(sessionId)
600
621
  if (!bucket) return
601
622
  await fs.rm(sessionDataFile(sessionId, bucket), { force: true })
623
+ const { deleteSessionAssets } = await import('./session-assets.mjs')
624
+ await deleteSessionAssets(bucket, sessionId)
602
625
  sessionBucketIndex.delete(sessionId)
603
626
  })
604
627
  }
@@ -116,6 +116,16 @@ export const workspaceTools = [
116
116
  }),
117
117
  executionMode: 'sequential',
118
118
  },
119
+ {
120
+ name: 'generate_image',
121
+ label: 'Generate image',
122
+ description: 'Generate images with the configured OpenRouter provider and save them as assets owned by the current conversation. Use this when the user explicitly asks for a generated bitmap image. The operation may incur provider charges.',
123
+ parameters: Type.Object({
124
+ prompt: Type.String({ description: 'Detailed image-generation prompt.' }),
125
+ model: Type.Optional(Type.String({ description: 'Optional OpenRouter image model ID. Defaults to google/gemini-2.5-flash-image.' })),
126
+ }),
127
+ executionMode: 'sequential',
128
+ },
119
129
  {
120
130
  name: 'present_files',
121
131
  label: 'Present files',
@@ -15,6 +15,7 @@ import {
15
15
  } from '../skills.mjs'
16
16
  import { getToolWorkspaceRoot } from '../utils/workspace.mjs'
17
17
  import { manageGlobalMemory } from '../global-memory.mjs'
18
+ import { generateSessionImages } from '../image-generation.mjs'
18
19
 
19
20
  const require = createRequire(import.meta.url)
20
21
 
@@ -1100,6 +1101,7 @@ export const toolHandlers = {
1100
1101
  write_file: toolWriteFile,
1101
1102
  edit_file: toolEditFile,
1102
1103
  run_command: toolRunCommand,
1104
+ generate_image: generateSessionImages,
1103
1105
  present_files: toolPresentFiles,
1104
1106
  activate_skill: toolActivateSkill,
1105
1107
  read_skill_resource: toolReadSkillResource,
@@ -1 +0,0 @@
1
- import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{D as t,Ft as n,Rt as r,Vt as i,a,dt as ee,l as o}from"./icons-C7j5jdKo.js";import{i as s,n as c}from"./react-vendor-VqdnQHHS.js";import{$ as te,H as l,Q as ne,ct as u,et as re,ht as d,nt as ie,tt as ae,ut as f}from"./index-DxNzlPmc.js";var p=e(i(),1),oe=s(),m=c(),h=144,g=82,_=4,v=8;function y(e){return JSON.stringify({provider:e.provider,modelId:e.id,api:e.api,baseUrl:e.baseUrl})}function b(e){if(!e)return{mode:`inherit`};try{let t=JSON.parse(e);return{mode:`fixed`,provider:String(t.provider||``),modelId:String(t.modelId||``),api:t.api?String(t.api):void 0,baseUrl:t.baseUrl?String(t.baseUrl):void 0}}catch{return{mode:`inherit`}}}function x(e){return!e||e.mode!==`fixed`?``:JSON.stringify({provider:e.provider,modelId:e.modelId,api:e.api,baseUrl:e.baseUrl})}function se(e){return e.name||`${e.provider}/${e.id}`}function S(){return{name:``,label:``,description:``,systemPrompt:``,allowedTools:[`read_file`,`grep_files`],maxRuntimeMs:`1800000`,maxToolCalls:`300`,enabledAsSubagent:!0,modelMode:`inherit`,fixedModelValue:``,thinkingLevel:`inherit`}}function ce(e){return{name:e.name,label:e.label,description:e.description??``,systemPrompt:e.systemPrompt??``,allowedTools:e.allowedTools??[],maxRuntimeMs:String(e.maxRuntimeMs??18e5),maxToolCalls:String(e.maxToolCalls??300),enabledAsSubagent:e.enabledAsSubagent,modelMode:e.model?.mode===`fixed`?`fixed`:`inherit`,fixedModelValue:x(e.model),thinkingLevel:e.thinkingLevel??`inherit`}}function le(e){return{name:e.name.trim().toLowerCase(),label:e.label.trim(),description:e.description.trim(),systemPrompt:e.systemPrompt.trim(),allowedTools:e.allowedTools,maxRuntimeMs:Number(e.maxRuntimeMs||18e5),maxToolCalls:Number(e.maxToolCalls||300),enabledAsSubagent:e.enabledAsSubagent,model:e.modelMode===`fixed`?b(e.fixedModelValue):{mode:`inherit`},thinkingLevel:e.thinkingLevel}}function C(e){return!!(e.name.trim()&&e.label.trim()&&e.allowedTools.length>0)}async function w(e,t){let n=await fetch(e,{...t,headers:{"content-type":`application/json`,...t?.headers}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||`请求失败`);return r}function T(){let[e,i]=(0,p.useState)([]),[s,c]=(0,p.useState)([]),[x,T]=(0,p.useState)(!1),[E,D]=(0,p.useState)(null),[O,k]=(0,p.useState)(()=>S()),[A,j]=(0,p.useState)(!1),[M,N]=(0,p.useState)(``),[P,F]=(0,p.useState)(!1),[I,ue]=(0,p.useState)(),[L,de]=(0,p.useState)([]),[fe,pe]=(0,p.useState)(`off`),[R,z]=(0,p.useState)(``),[B,V]=(0,p.useState)(null),[H,U]=(0,p.useState)(null);async function W(){let[e,t]=await Promise.all([w(`/api/agent-profiles`),w(`/api/agent-profiles/available-tools`)]);i(e.agents),c(t.tools)}(0,p.useEffect)(()=>{let e=!1;async function t(){try{let[t,n]=await Promise.all([w(`/api/agent-profiles`),w(`/api/agent-profiles/available-tools`)]);if(e)return;i(t.agents),c(n.tools)}catch(t){e||z(t instanceof Error?t.message:d(`requestFailed`))}}return t(),()=>{e=!0}},[]),(0,p.useEffect)(()=>{let e=!1;async function t(){try{let t=await re(),n=await te(t);de(n);let r=await ae(t),i=r.model??await ie(t)??n[0];if(e)return;ue(i),pe(r.thinkingLevel??ne(i))}catch{}}return t(),()=>{e=!0}},[]),(0,p.useEffect)(()=>{if(!B)return;let e=()=>{V(null),U(null)},t=t=>{t.key===`Escape`&&e()};return window.addEventListener(`click`,e),window.addEventListener(`blur`,e),window.addEventListener(`resize`,e),window.addEventListener(`scroll`,e,!0),document.addEventListener(`keydown`,t),()=>{window.removeEventListener(`click`,e),window.removeEventListener(`blur`,e),window.removeEventListener(`resize`,e),window.removeEventListener(`scroll`,e,!0),document.removeEventListener(`keydown`,t)}},[B]);let G=(0,p.useMemo)(()=>e.find(e=>e.id===E)??null,[e,E]),K=(0,p.useMemo)(()=>e.find(e=>e.id===B)??null,[e,B]),q=!!G?.readonly,J=!!(G?.readonly&&!G?.builtin),Y=(0,p.useMemo)(()=>L.find(e=>y(e)===O.fixedModelValue),[O.fixedModelValue,L]),X=O.modelMode===`fixed`&&!!Y&&Y?.reasoning!==!0;function me(e,t){if(e.stopPropagation(),B===t){V(null),U(null);return}let n=e.currentTarget.getBoundingClientRect(),r=Math.max(v,Math.min(n.right-h,window.innerWidth-h-v)),i=n.bottom+_,a=n.top-_-g;U({left:r,top:i+g<=window.innerHeight-v?i:Math.max(v,a)}),V(t)}function Z(e,t){k(n=>({...n,[e]:t}))}function he(e){k(t=>({...t,allowedTools:t.allowedTools.includes(e)?t.allowedTools.filter(t=>t!==e):[...t.allowedTools,e]}))}function ge(){D(null),k(S()),N(``),z(``),T(!0)}function Q(e){D(e.id),k(ce(e)),N(``),z(``),T(!0)}function $(){A||P||(T(!1),D(null),k(S()),N(``))}async function _e(){let e=M.trim();if(!e){z(d(`aiFillAgentInputRequired`));return}if(!I){z(d(`aiFillAgentNoModel`));return}F(!0),z(``);try{let t=await w(`/api/agent-profiles/ai-fill`,{method:`POST`,body:JSON.stringify({instruction:e,model:I,thinkingLevel:fe})});k(e=>({...e,name:t.agent.name,label:t.agent.label,description:t.agent.description,systemPrompt:t.agent.systemPrompt}))}catch(e){z(e instanceof Error?e.message:d(`aiFillAgentFailed`))}finally{F(!1)}}async function ve(){if(C(O)){j(!0),z(``);try{let e=G?.builtin?{model:O.modelMode===`fixed`?b(O.fixedModelValue):{mode:`inherit`}}:le({...O,thinkingLevel:X?`off`:O.thinkingLevel});E?await w(`/api/agent-profiles/${encodeURIComponent(E)}`,{method:`PATCH`,body:JSON.stringify(e)}):await w(`/api/agent-profiles`,{method:`POST`,body:JSON.stringify(e)}),$(),await W()}catch(e){z(e instanceof Error?e.message:d(`requestFailed`))}finally{j(!1)}}}async function ye(e){if(e.builtin||e.readonly)return;let t=!e.enabledAsSubagent,n=e.enabledAsSubagent;i(n=>n.map(n=>n.id===e.id?{...n,enabledAsSubagent:t}:n)),V(null);try{await w(`/api/agent-profiles/${encodeURIComponent(e.id)}`,{method:`PATCH`,body:JSON.stringify({enabledAsSubagent:t})})}catch(t){i(t=>t.map(t=>t.id===e.id?{...t,enabledAsSubagent:n}:t)),z(t instanceof Error?t.message:d(`requestFailed`))}}async function be(e){if(!(e.builtin||e.readonly)&&await l({description:d(`confirmDeleteAgent`),confirmLabel:d(`confirmDelete`),cancelLabel:d(`cancel`),variant:`destructive`})){z(``);try{await w(`/api/agent-profiles/${encodeURIComponent(e.id)}`,{method:`DELETE`}),await W()}catch(e){z(e instanceof Error?e.message:d(`requestFailed`))}}}return x?(0,m.jsxs)(`div`,{className:`quickforge-settings-stack`,children:[(0,m.jsx)(`div`,{className:`quickforge-settings-heading`,children:(0,m.jsxs)(`h3`,{className:`quickforge-settings-title`,children:[G?.builtin?d(`builtinAgentModelSettings`):d(G?`editAgent`:`createAgent`),(0,m.jsx)(u,{label:G?.builtin?d(`builtinAgentModelOnly`):G?.readonly?d(`readonlyAgentDescription`):d(`agentsDescription`)})]})}),(0,m.jsxs)(`section`,{className:`quickforge-settings-section`,"aria-label":d(G?`editAgent`:`createAgent`),children:[(0,m.jsxs)(`div`,{className:`quickforge-settings-toolbar`,children:[(0,m.jsxs)(`button`,{className:`quickforge-settings-button quickforge-settings-button-secondary`,type:`button`,onClick:$,disabled:A||P,children:[(0,m.jsx)(r,{className:`mr-2 size-4`}),d(`back`)]}),(0,m.jsxs)(`div`,{className:`quickforge-settings-row-main`,children:[(0,m.jsx)(`div`,{className:`quickforge-settings-row-title`,children:G?.builtin?d(`builtinAgentModelSettings`):d(G?`editAgent`:`createAgent`)}),G?.builtin?(0,m.jsx)(`div`,{className:`quickforge-settings-row-description`,children:d(`builtinAgentModelOnly`)}):G?.readonly?(0,m.jsx)(`div`,{className:`quickforge-settings-row-description`,children:d(`readonlyAgentDescription`)}):null]})]}),(0,m.jsx)(`div`,{className:`px-5 py-4`,children:(0,m.jsxs)(`div`,{className:`space-y-4`,children:[(0,m.jsxs)(`div`,{className:`rounded-2xl border border-border bg-muted/20 p-3`,children:[(0,m.jsxs)(`div`,{className:`mb-2 flex items-center gap-2 text-sm font-medium text-foreground`,children:[(0,m.jsx)(o,{className:`size-4 text-primary`}),d(`aiFillAgent`),(0,m.jsx)(u,{label:d(`aiFillAgentDescription`)})]}),(0,m.jsx)(`textarea`,{className:`min-h-20 w-full resize-y rounded-xl border border-input bg-background px-3 py-2 text-sm outline-none transition-colors placeholder:text-muted-foreground/65 focus:border-ring disabled:opacity-60`,value:M,disabled:q||P,onChange:e=>N(e.target.value),placeholder:d(`aiFillAgentPlaceholder`)}),(0,m.jsx)(`div`,{className:`mt-2 flex justify-end`,children:(0,m.jsxs)(f,{variant:`outline`,size:`sm`,onClick:()=>void _e(),disabled:q||P||!M.trim(),children:[(0,m.jsx)(o,{className:`mr-1 size-3.5`}),d(P?`aiFillAgentLoading`:`aiFillAgent`)]})})]}),(0,m.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentName`),(0,m.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.name,disabled:q,onChange:e=>Z(`name`,e.target.value),placeholder:`reviewer`})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentLabel`),(0,m.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.label,disabled:q,onChange:e=>Z(`label`,e.target.value),placeholder:d(`agentLabelPlaceholder`)})]})]}),G?(0,m.jsxs)(`div`,{className:`rounded-xl border border-border bg-muted/20 px-3 py-2 text-sm`,children:[(0,m.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:d(`agentSourcePath`)}),(0,m.jsx)(`div`,{className:`mt-1 truncate font-mono text-xs text-foreground`,title:G.source?`${G.source}${G.relativePath?` · ${G.relativePath}`:``}`:void 0,children:G.source?`${G.source}${G.relativePath?` · ${G.relativePath}`:``}`:G.builtin?d(`builtinAgent`):`-`})]}):null,(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentDescription`),(0,m.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.description,disabled:q,onChange:e=>Z(`description`,e.target.value)})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentSystemPrompt`),(0,m.jsx)(`textarea`,{className:`mt-1 min-h-36 w-full resize-y rounded-xl border border-input bg-background px-3 py-2 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.systemPrompt,disabled:q,onChange:e=>Z(`systemPrompt`,e.target.value)})]}),(0,m.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentModelMode`),(0,m.jsxs)(`select`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.modelMode,disabled:J,onChange:e=>Z(`modelMode`,e.target.value),children:[(0,m.jsx)(`option`,{value:`inherit`,children:d(`agentModelInherit`)}),(0,m.jsx)(`option`,{value:`fixed`,children:d(`agentModelFixed`)})]})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentFixedModel`),(0,m.jsxs)(`select`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.fixedModelValue,disabled:J||O.modelMode!==`fixed`,onChange:e=>Z(`fixedModelValue`,e.target.value),children:[(0,m.jsx)(`option`,{value:``,children:d(`agentModelInherit`)}),L.map(e=>(0,m.jsx)(`option`,{value:y(e),children:se(e)},y(e)))]})]})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentThinkingLevel`),(0,m.jsxs)(`select`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:X&&O.thinkingLevel!==`inherit`?`off`:O.thinkingLevel,disabled:q||X,onChange:e=>Z(`thinkingLevel`,e.target.value),children:[(0,m.jsx)(`option`,{value:`inherit`,children:d(`agentThinkingInherit`)}),(0,m.jsx)(`option`,{value:`off`,children:d(`thinkingOff`)}),(0,m.jsx)(`option`,{value:`low`,children:d(`thinkingLow`)}),(0,m.jsx)(`option`,{value:`medium`,children:d(`thinkingMedium`)}),(0,m.jsx)(`option`,{value:`high`,children:d(`thinkingHigh`)}),(0,m.jsx)(`option`,{value:`xhigh`,children:d(`thinkingXHigh`)})]}),(0,m.jsx)(`span`,{className:`mt-1 block text-xs text-muted-foreground`,children:d(X?`agentThinkingUnsupported`:`agentThinkingDescription`)})]}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`div`,{className:`mb-2 text-sm font-medium text-foreground`,children:d(`allowedTools`)}),(0,m.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2`,children:s.map(e=>(0,m.jsxs)(`label`,{className:`flex items-start gap-2 rounded-xl border border-border bg-muted/20 p-3 text-sm disabled:opacity-60`,children:[(0,m.jsx)(`input`,{type:`checkbox`,className:`mt-1`,disabled:q,checked:O.allowedTools.includes(e.name),onChange:()=>he(e.name)}),(0,m.jsxs)(`span`,{children:[(0,m.jsx)(`span`,{className:`font-medium text-foreground`,children:e.label}),(0,m.jsx)(`span`,{className:`ml-2 font-mono text-xs text-muted-foreground`,children:e.name}),e.riskLevel===`dangerous`?(0,m.jsx)(`span`,{className:`ml-2 rounded-full bg-amber-500/10 px-2 py-0.5 text-xs text-amber-700`,children:d(`highRiskTool`)}):null,(0,m.jsx)(`span`,{className:`mt-1 block text-xs text-muted-foreground`,children:e.description})]})]},e.name))})]}),(0,m.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`maxRuntimeMs`),(0,m.jsx)(`input`,{type:`number`,className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.maxRuntimeMs,disabled:q,onChange:e=>Z(`maxRuntimeMs`,e.target.value)})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`maxToolCalls`),(0,m.jsx)(`input`,{type:`number`,className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.maxToolCalls,disabled:q,onChange:e=>Z(`maxToolCalls`,e.target.value)})]})]}),(0,m.jsxs)(`label`,{className:`flex items-center gap-2 text-sm text-foreground`,children:[(0,m.jsx)(`input`,{type:`checkbox`,checked:O.enabledAsSubagent,disabled:q,onChange:e=>Z(`enabledAsSubagent`,e.target.checked)}),d(`enabledAsSubagent`)]}),R?(0,m.jsx)(`div`,{className:`rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:R}):null]})}),(0,m.jsxs)(`div`,{className:`quickforge-settings-divider flex justify-end gap-2 px-5 py-4`,children:[(0,m.jsx)(f,{variant:`outline`,onClick:$,disabled:A||P,children:d(`cancel`)}),(0,m.jsx)(f,{onClick:ve,disabled:A||P||J||!G?.builtin&&!C(O)||O.modelMode===`fixed`&&!O.fixedModelValue,children:d(`save`)})]})]})]}):(0,m.jsxs)(`div`,{className:`quickforge-settings-stack`,children:[(0,m.jsxs)(`section`,{className:`quickforge-settings-section`,"aria-label":d(`agentsTab`),children:[(0,m.jsxs)(`div`,{className:`quickforge-settings-toolbar`,children:[(0,m.jsxs)(`div`,{className:`quickforge-settings-row-main`,children:[(0,m.jsxs)(`div`,{className:`quickforge-settings-row-title`,children:[(0,m.jsx)(n,{className:`size-4 text-primary`}),d(`agentsTab`)]}),(0,m.jsx)(`div`,{className:`quickforge-settings-row-description`,children:d(`agentsDescription`)})]}),(0,m.jsx)(`button`,{className:`quickforge-settings-button quickforge-settings-button-primary`,type:`button`,onClick:ge,children:d(`createAgent`)})]}),R?(0,m.jsx)(`div`,{className:`quickforge-settings-alert quickforge-settings-warning-attached`,children:R}):null,e.length===0?(0,m.jsx)(`div`,{className:`quickforge-settings-empty-row`,children:d(`loading`)}):e.map(e=>(0,m.jsxs)(`div`,{className:`quickforge-settings-list-item quickforge-agent-profile-row`,role:`button`,tabIndex:0,onClick:()=>Q(e),onKeyDown:t=>{t.target===t.currentTarget&&(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),Q(e))},children:[(0,m.jsx)(`div`,{className:`quickforge-settings-list-item-main quickforge-agent-profile-row-main`,children:(0,m.jsxs)(`div`,{className:`quickforge-agent-profile-summary`,children:[(0,m.jsx)(`span`,{className:`quickforge-agent-profile-label`,title:e.label,children:e.label}),e.description?(0,m.jsx)(`span`,{className:`quickforge-agent-profile-description`,title:e.description,children:e.description}):null]})}),(0,m.jsxs)(`div`,{className:`quickforge-settings-list-item-actions`,onClick:e=>e.stopPropagation(),children:[(0,m.jsxs)(`label`,{className:`quickforge-settings-switch`,"aria-disabled":e.builtin||e.readonly?`true`:`false`,title:e.enabledAsSubagent?d(`disableAsSubagent`):d(`enableAsSubagent`),children:[(0,m.jsx)(`input`,{type:`checkbox`,checked:e.enabledAsSubagent,disabled:e.builtin||e.readonly,onChange:()=>void ye(e)}),(0,m.jsx)(`span`,{"aria-hidden":`true`})]}),(0,m.jsx)(`button`,{className:`quickforge-settings-icon-action`,type:`button`,onClick:t=>me(t,e.id),title:d(`moreActions`),"aria-label":d(`moreActions`),"aria-haspopup":`menu`,"aria-expanded":B===e.id,children:(0,m.jsx)(ee,{className:`size-4`})})]})]},e.id))]}),K&&H?(0,oe.createPortal)((0,m.jsxs)(`div`,{className:`fixed z-50 w-36 overflow-hidden rounded-xl border border-border bg-popover py-1 text-sm shadow-quickforge`,style:{left:H.left,top:H.top},role:`menu`,"aria-label":d(`moreActions`),onClick:e=>e.stopPropagation(),children:[(0,m.jsxs)(`button`,{className:`flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50`,type:`button`,role:`menuitem`,disabled:K.readonly&&!K.builtin,onClick:()=>{V(null),U(null),Q(K)},children:[(0,m.jsx)(t,{className:`size-3.5`}),K.builtin?d(`builtinAgentModelSettings`):d(`editTask`)]}),(0,m.jsxs)(`button`,{className:`flex w-full items-center gap-2 px-3 py-2 text-left text-destructive hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50`,type:`button`,role:`menuitem`,disabled:K.builtin||K.readonly,onClick:()=>{V(null),U(null),be(K)},children:[(0,m.jsx)(a,{className:`size-3.5`}),d(`delete`)]})]}),document.body):null]})}export{T as AgentProfilesPage};