alchemylab 2.1.0 → 2.1.1
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/dist/111.js +11 -12
- package/dist/24.js +34 -232
- package/dist/293.js +52 -50
- package/dist/356.js +11 -11
- package/dist/414.js +16 -16
- package/dist/478.js +33 -33
- package/dist/579.js +7 -7
- package/dist/60.js +8 -8
- package/dist/729.js +17 -17
- package/dist/776.js +1 -1
- package/dist/800.js +2 -2
- package/dist/816.js +2 -3
- package/dist/939.js +15 -15
- package/dist/991.js +12 -12
- package/dist/994.js +22 -22
- package/dist/cli.js +7309 -7656
- package/dist/document-textconv.js +372 -413
- package/dist/http-server.js +7618 -7708
- package/dist/index.js +7664 -7754
- package/dist/mcp-direct.js +7462 -7552
- package/dist/plugins/hooks/plugin.js +1 -1
- package/dist/plugins/system-logger.js +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as t from"path";import{join as e}from"path";import{readFileSync as n,existsSync as o}from"fs";const a=2e5;function r(t,e,n){if(e.length<=a)return null;const o=(e.length/1048576).toFixed(1);return[`[AlchemyLab did not pass this result on: ${e.length.toLocaleString()} characters (about ${o} MB).]`,"",`"${t}" returned more than fits usefully in context, so it was held back rather than`,"filling the window. The first part of it was:","",e.slice(0,2e3),"","[...held back...]","",n?`The whole result is stored: ${n}\nRead the part you need with \`result_inspect\` (operation "read", that address, plus a line range or \`find\`), or ask this connection for a summary or a narrower query.`:"Ask this connection for a path, a summary, or a narrower query instead of the whole result."].join("\n")}let s=null,i=null;function l(t,e){fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).catch(
|
|
1
|
+
import*as t from"path";import{join as e}from"path";import{readFileSync as n,existsSync as o}from"fs";const a=2e5;function r(t,e,n){if(e.length<=a)return null;const o=(e.length/1048576).toFixed(1);return[`[AlchemyLab did not pass this result on: ${e.length.toLocaleString()} characters (about ${o} MB).]`,"",`"${t}" returned more than fits usefully in context, so it was held back rather than`,"filling the window. The first part of it was:","",e.slice(0,2e3),"","[...held back...]","",n?`The whole result is stored: ${n}\nRead the part you need with \`result_inspect\` (operation "read", that address, plus a line range or \`find\`), or ask this connection for a summary or a narrower query.`:"Ask this connection for a path, a summary, or a narrower query instead of the whole result."].join("\n")}let s=null,i=null;function l(t,e){fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).catch(e=>{console.warn("[hooks-plugin] hook event delivery failed",t,e instanceof Error?e.message:String(e))})}async function c(t,e){try{await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(3e4)})}catch(e){e instanceof Error&&"TimeoutError"===e.name&&console.warn(`[hooks-plugin] after-event POST to ${t} aborted after 30000ms; blocking hooks may not have run`)}}let u=null;async function p(t){if(u&&Date.now()-u.at<6e4)return u.names;try{const e=await fetch(`${t}/api/mcp/namespaces`,{signal:AbortSignal.timeout(5e3)}),n=await e.json(),o=n?.data?.namespaces??[];return u={at:Date.now(),names:Array.isArray(o)?o:[]},u.names}catch{return u={at:Date.now(),names:u?.names??[]},u.names}}const h=new Set(["read","write","edit","patch","multiedit","apply_patch","bash","grep","glob","list","ls","todowrite","todoread","webfetch","websearch","task","skill","invalid"]);function f(t,e){return e.some(e=>t.startsWith(`${e}_`)&&t.length>e.length+1)}async function d(t,e){return!!f(e,await p(t))||!e.startsWith("alchemy_")&&!h.has(e)&&(u=null,f(e,await p(t)))}const y=[1,Number.MAX_SAFE_INTEGER];function m(t){return(Array.isArray(t?.operations)?t.operations:[]).filter(t=>"string"==typeof t?.path&&t.path).map(t=>({path:t.path}))}const g=(()=>{const t={kind:"edit",files:(t,e)=>function(t,e){const n=new Set;"string"==typeof t?.filePath&&n.add(t.filePath);for(const t of Array.isArray(e?.files)?e.files:[]){const e=t?.movePath??t?.filePath;"string"==typeof e&&"delete"!==t?.type&&n.add(e)}return[...n].map(t=>({path:t}))}(t,e)},e={kind:"edit",files:m};return{read:{kind:"read",files:t=>{if("string"!=typeof t?.filePath)return[];const e=Number(t.offset)>=1?Math.floor(Number(t.offset)):1,n=Number(t.limit)>=1?Math.floor(Number(t.limit)):2e3;return[{path:t.filePath,lines:[e,e+n-1]}]}},edit:t,write:t,multiedit:t,patch:t,apply_patch:t,alchemy_read_files:{kind:"read",files:t=>(Array.isArray(t?.paths)?t.paths:[]).filter(t=>"string"==typeof t?.path&&t.path).map(t=>{if(void 0!==t.lines){const e=function(t){const e=/^\s*(\d+)\s*(?:-\s*(\d+)\s*)?$/.exec("string"==typeof t?t:"");if(!e)return null;const n=Number(e[1]),o=void 0===e[2]?n:Number(e[2]);return n>=1&&o>=n?[n,o]:null}(t.lines);return e?{path:t.path,lines:e}:{path:t.path}}return t.symbol?{path:t.path}:{path:t.path,lines:y}})},alchemy_write_file:{kind:"edit",files:t=>"string"==typeof t?.path&&t.path?[{path:t.path}]:[]},alchemy_replace_in_files:e,alchemy_replace_strings:e,alchemy_replace_lines:e,alchemy_insert_lines:e,alchemy_modify_file:e}})();function w(t,e,n){const o=g[t];if(!o)return null;const a=new Set,r=o.files(e??{},n).filter(t=>!a.has(t.path)&&(a.add(t.path),!0));return r.length>0?{kind:o.kind,files:r}:null}function b(t,e){if("string"==typeof t?.output)return void(t.output+=`\n\n${e}`);const n=Array.isArray(t?.content)?t.content:null;if(!n||t.isError)return;const o=n.find(t=>"text"===t?.type&&"string"==typeof t.text);o?o.text+=`\n\n${e}`:n.push({type:"text",text:e})}export default async({directory:u})=>{const p=u||process.cwd();if(s===p&&i)return i;s=p;const h=function(t){try{const a=e(t,".alchemy_lab","action-server.json");if(!o(a))return 3456;const r=JSON.parse(n(a,"utf-8"));if("number"==typeof r.port&&r.port>0&&r.port<65536)return r.port}catch{}return 3456}(p),f=`http://localhost:${h}`;!async function(t,e,n){try{const o=await fetch(`http://localhost:${e}/health`,{signal:AbortSignal.timeout(1500)});if(!o.ok)return!1;const a=await o.json(),r="string"==typeof a?.projectPath?a.projectPath:"",s=!!r&&(r===t||r.endsWith(t)||t.endsWith(r));return s||n?.(`[alchemy hooks] port ${e} serves ${r||"an unknown project"}, not ${t}: hooks would fire into the wrong project`),s}catch{return n?.(`[alchemy hooks] nothing answered /health on port ${e}: hook events will not be delivered`),!1}}(p,h,t=>console.error(t));const y=`${f}/api/hook-event`,m=`${f}/api/tool-event`,g={"tool.execute.before":async(e,n)=>{const o=e?.tool||e?.name;if(!o)return;const a=e?.args||e?.input||{},r=e?.sessionID||"";"bash"===o&&n?.args&&"object"==typeof n.args&&await async function(e,n,o,a){const r="string"==typeof a.workdir&&a.workdir.trim()?a.workdir.trim():".";if(function(e,n){const o=t.resolve(e),a=t.resolve(o,n);if(a!==o&&!a.startsWith(o+t.sep))return!1;const r=t.join(o,".alchemy_lab");return!(a===r||a.startsWith(r+t.sep))}(n,r))try{const s=await fetch(`${e}/api/session-workdir?sessionId=${encodeURIComponent(o)}`,{signal:AbortSignal.timeout(5e3)}),i=s.ok?await s.json():null,l=i?.data?.workdir;if("string"!=typeof l||!l)return;const c=t.relative(t.resolve(n),t.resolve(n,r));a.workdir=c&&!c.startsWith("..")?t.join(l,c):l}catch{}}(f,p,r,n.args),await d(f,o)&&await async function(t,e,n,o,a){let r;try{const s=await fetch(`${t}/api/mcp/gate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({toolName:e,sessionId:n,args:o,callId:a}),signal:AbortSignal.timeout(66e4)}),i=await s.json();if(r=i?.data,!s.ok||!r)throw new Error(i?.error||`gate returned HTTP ${s.status}`)}catch(t){const n=t instanceof Error?t.message:String(t);throw console.warn("[hooks-plugin] MCP gate unreachable, refusing the call",e,n),new Error(`${e} was not run: its approval gate could not be reached (${n}).`)}if(!r.allow)throw new Error(`${e} was not run: ${r.reason}.`)}(f,o,r,a,e?.callID||""),l(m,{toolName:o,sessionId:r,input:a,timing:"before"})},"tool.execute.after":async(t,e)=>{const n=t?.tool||t?.name;if(!n)return;const o=e?w(String(n),t?.args,e.metadata):null;if(o){const n=await async function(t,e,n){if(!e)return null;try{const o=await fetch(`${t}/api/parallel-note`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:e,tool:n.kind,files:n.files}),signal:AbortSignal.timeout(4e3)});if(!o.ok)return null;const a=await o.json();return"string"==typeof a?.data?.note&&a.data.note?a.data.note:null}catch{return null}}(f,t?.sessionID||"",o);n&&b(e,n)}const s=e?.content?.[0]?.text;if("string"==typeof s&&s.length>a){const o=await async function(t,e,n,o){try{const a=await fetch(`${t}/api/results/spill`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:n,toolName:e,...o?{callId:o}:{}}),signal:AbortSignal.timeout(3e4)});if(!a.ok)return;const r=await a.json();return r?.data?.address??void 0}catch{return}}(f,String(n),s,t?.callID),a=r(String(n),s,o);if(a)return e.content[0].text=a,void await c(m,{toolName:n,sessionId:t?.sessionID||"",input:t?.args||t?.input||{},output:a,timing:"after",...t?.callID?{callId:t.callID}:{}})}var i;s?await c(m,{toolName:n,sessionId:t?.sessionID||"",input:t?.args||t?.input||{},output:(i=s,i.length<=8192?i:i.slice(0,6144)+"\n...[truncated]...\n"+i.slice(-2048)),timing:"after",callId:t?.callID||""}):t?.callID&&await d(f,n)&&await c(m,{toolName:n,sessionId:t?.sessionID||"",input:t?.args||t?.input||{},timing:"after",callId:t.callID})},event:async({event:t})=>{switch(t.type){case"session.idle":{const a=t.properties.sessionID||"",{output:r,lastMessageId:s}=await async function(t,a){try{const r=e(a,".alchemy_lab","opencode.json");if(!o(r))return{output:""};const s=JSON.parse(n(r,"utf8")),i=s.port?`http://127.0.0.1:${s.port}`:null;if(!i)return{output:""};const l=process.env.OPENCODE_SERVER_PASSWORD,c=l?{Authorization:`Basic ${Buffer.from(`${process.env.OPENCODE_SERVER_USERNAME||"opencode"}:${l}`).toString("base64")}`}:{},u=await fetch(`${i}/session/${t}/message?limit=5`,{headers:c});if(!u.ok)return{output:""};const p=(await u.json()).filter(t=>"assistant"===t.info.role);return{output:p.flatMap(t=>t.parts).filter(t=>"text"===t.type&&t.text).map(t=>t.text).join("\n"),lastMessageId:p[0]?.info?.id}}catch{return{output:""}}}(a,p);return void l(y,{eventType:"session_idle",sessionId:a,variables:{sessionId:a,lastOutput:r,lastMessageId:s||""}})}case"session.created":{const e=t.properties.info?.id||"";if(!e)return;return void setTimeout(()=>{l(y,{eventType:"session_created",sessionId:e,variables:{sessionId:e}})},500)}}}};return i=g,g};export const internals={ceilingNotice:r,MCP_RESULT_CEILING_CHARS:a,parallelNoteCall:w,appendToResult:b};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{appendFileSync as e,mkdirSync as s}from"fs";import{join as t}from"path";const n="true"===process.env.ALCHEMY_DEBUG_LOGS,r=(r,o,l)=>{if(!n)return;const i=`[${(new Date).toISOString()}] ${o}${l?" "+JSON.stringify(l):""}\n`;try{s(t(r,".."),{recursive:!0}),e(r,i)}catch(e){}};export default async({directory:o})=>{const l=(e=>t(e,".alchemy_lab","system-prompt.log"))(o);return r(l,"system-logger plugin loaded",{directory:o}),{"experimental.chat.system.transform":async(o,i)=>{const a=i.system.some(
|
|
1
|
+
import{appendFileSync as e,mkdirSync as s}from"fs";import{join as t}from"path";const n="true"===process.env.ALCHEMY_DEBUG_LOGS,r=(r,o,l)=>{if(!n)return;const i=`[${(new Date).toISOString()}] ${o}${l?" "+JSON.stringify(l):""}\n`;try{s(t(r,".."),{recursive:!0}),e(r,i)}catch(e){}};export default async({directory:o})=>{const l=(e=>t(e,".alchemy_lab","system-prompt.log"))(o);return r(l,"system-logger plugin loaded",{directory:o}),{"experimental.chat.system.transform":async(o,i)=>{const a=i.system.some(e=>e.includes("head of engineering"));if(n&&r(l,"Alchemist detection",{isAlchemist:a,sessionID:o.sessionID,systemParts:i.system.length,partLengths:i.system.map(e=>e.length),hasSpecialist:i.system.map(e=>e.includes("specialist_ops"))}),a&&(function(e){for(let s=0;s<e.length;s++){let t=e[s];t=t.replace(/^- When doing file search, prefer to use the Task tool.*$/gm,""),t=t.replace(/^- You should proactively use the Task tool.*$/gm,""),t=t.replace(/^- VERY IMPORTANT:.*use the Task tool instead of running search commands directly\.$/gm,""),t=t.replace(/^- If the user specifies that they want you to run tools "in parallel".*multiple Task tool calls\.$/gm,""),t=t.replace(/<example>\n.*?\[Uses the Task tool.*?<\/example>/gs,""),t=t.replace(/\n{3,}/g,"\n\n"),e[s]=t}}(i.system),r(l,"Stripped Task tool references for alchemist session",{sessionID:o.sessionID})),!n)return;const c=i.system.some(e=>e.includes("referenced the following")||e.includes("entity")),m=["=".repeat(80),`Timestamp: ${(new Date).toISOString()}`,`Session: ${o.sessionID||"N/A"}`,`Model: ${o.model?.providerID||"?"}/${o.model?.modelID||"?"}`,`System parts: ${i.system.length}`,`Has entity context: ${c}`,"-".repeat(80),...i.system.map((e,s)=>{const t=e.length>500?e.slice(0,250)+"\n...[truncated]...\n"+e.slice(-250):e;return`--- Part ${s} (${e.length} chars) ---\n${t}`}),"=".repeat(80),""];try{s(t(l,".."),{recursive:!0}),e(l,m.join("\n")+"\n");const n=t(l,"..","system-prompt-full.log"),r=[`Timestamp: ${(new Date).toISOString()}`,`Session: ${o.sessionID||"N/A"}`,`IsAlchemist: ${a}`,`System parts: ${i.system.length}`,...i.system.map((e,s)=>`--- Part ${s} (${e.length} chars) ---\n${e}`),"=".repeat(80)],{writeFileSync:c}=require("fs");c(n,r.join("\n")+"\n")}catch(e){}}}};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemylab",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "AlchemyLab: the agentic workbench. Work is a request, with criteria, gates and a receipt.",
|
|
6
6
|
"author": "Testfire Software",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": "^1.7.0",
|
|
48
|
-
"alchemylab-opencode": "^1.0.
|
|
48
|
+
"alchemylab-opencode": "^1.0.23",
|
|
49
49
|
"chalk": "^4.1.2",
|
|
50
50
|
"compression": "^1.8.1",
|
|
51
51
|
"cors": "^2.8.5",
|