allwright 1.133.0 → 1.134.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 (2) hide show
  1. package/bin/allwright.js +5 -5
  2. package/package.json +1 -1
package/bin/allwright.js CHANGED
@@ -1722,14 +1722,14 @@ ${(ee.description||"").slice(0,500)}`)}catch{_.push(`${z}: (could not read from
1722
1722
 
1723
1723
  `)+`
1724
1724
 
1725
- Answer with ONLY a fenced json block:
1726
- {"order": ["KEY", ...], "chains": [["KEY","KEY"], ...], "dependencies": [["BLOCKED","BLOCKED_BY"], ...], "reasoning": "..."}
1725
+ Answer with ONLY a fenced json block, "reasoning" FIRST - work out and state your groupings in words before you commit to the arrays, so the arrays actually match what you just said instead of drifting from it:
1726
+ {"reasoning": "...", "order": ["KEY", ...], "chains": [["KEY","KEY"], ...], "dependencies": [["BLOCKED","BLOCKED_BY"], ...]}
1727
1727
 
1728
1728
  Rules:
1729
+ - "reasoning": 2-3 short lines a person can check your plan against. Every pairing or dependency you name here MUST show up in "chains" or "dependencies" below - a reasoning line with no matching array entry means the group never actually happens (found 2026-08-29: reasoning named a real schema dependency between two tickets, "chains" left them both solo, and neither got the head start the other one needed).
1729
1730
  - "order": every key exactly once. Prerequisites before the work that needs them.
1730
- - "chains": tickets one engineer would do in one sitting, in the same part of the code, ADJACENT in your order. Chained tickets share a warm agent session, so group real neighbours - and leave a ticket out of every chain when unsure. Solo tickets appear in no chain.
1731
- - "dependencies": ONLY hard prerequisites, where the blocked ticket CANNOT pass if the other failed. An empty list is the normal answer.
1732
- - "reasoning": 2-3 short lines a person can check your plan against.`,G=await N.adapter.run({repoPath:g.repoPath,prompt:U,timeoutSeconds:90,bin:N.bin,mode:"read_only",maxTurns:3,model:N.adapter.fastModel,sessionId:(0,Vn.randomUUID)(),onLog:()=>{}});_e({surface:"autopilot_plan",projectId:g.project.id,agentId:N.adapter.id,refKind:"none",refLabel:`${d.issueKeys.length} tickets`,...ve(G)});let H=th(G.resultText,d.issueKeys);if(!H){S.sendAutopilotPlanResult(d.requestId,{ok:!1,error:"the planner's answer could not be parsed - the queue keeps its given order"});return}let{saveBatchPlan:C}=await Promise.resolve().then(()=>(Nc(),Bb));C({batchId:d.batchId,projectId:d.projectId,order:H.order,chains:H.chains,dependencies:H.dependencies,reasoning:H.reasoning}),S.sendAutopilotPlanResult(d.requestId,{ok:!0,...H})}catch(g){S.sendAutopilotPlanResult(d.requestId,{ok:!1,error:g instanceof Error?g.message:String(g)})}},onChatDiagnostic:async d=>{try{let{runDiagnostic:g}=await Promise.resolve().then(()=>(Uh(),Lv)),y=await g({command:d.command,args:d.args});b.info(`chat diagnostic (user-approved): ${y.commandLine}`),S.sendChatDiagnosticResult(d.requestId,y)}catch(g){S.sendChatDiagnosticResult(d.requestId,{ok:!1,output:g instanceof Error?g.message:String(g)})}},onChatStepRun:async d=>{let g=D.lookup(d.projectId,d.environmentName);if(!g){S.sendChatStepRunResult(d.requestId,{ok:!1,error:`this runner has no environment "${d.environmentName}" for that project`});return}let{environment:y}=g,_=y.steps[d.stepIndex];if(!_||_.name!==d.stepName){S.sendChatStepRunResult(d.requestId,{ok:!1,error:`step ${d.stepIndex+1} of "${y.name}" is ${_?`"${_.name}"`:"not there"} on this machine, not "${d.stepName}" - the recipe changed since the chat read it. Reopen the environment and ask again.`});return}let N=`${d.projectId}:${y.name}:${d.stepIndex}`;if(P.has(N)){S.sendChatStepRunResult(d.requestId,{ok:!1,stepName:_.name,error:"that step is already running here - wait for it to finish"});return}P.add(N),b.info(`chat step re-run (user-approved): [${y.name}] ${_.name}`);try{let{runSavedStep:U}=await Promise.resolve().then(()=>(Is(),xy)),G=await U({repoPath:g.repoPath,repoName:g.project.repoName,environmentName:y.name,workdir:y.workdir,branch:y.branch,step:_,cfg:t,timeoutMs:Math.min(y.timeoutSeconds*1e3,YC)});S.sendChatStepRunResult(d.requestId,{ok:!0,stepName:_.name,...G})}catch(U){S.sendChatStepRunResult(d.requestId,{ok:!1,stepName:_.name,error:U instanceof Error?U.message:String(U)})}finally{P.delete(N)}},onChatCancel:d=>{let g=L.get(d.turnId);g&&(g.abort(),b.info(`chat turn ${d.turnId} stopped from the dashboard`))},onChatTurn:async d=>{let g=A(d.projectId);if(!g){S.sendChatTurnResult(d.requestId,{ok:!1,sessionId:d.sessionId??"",reply:"",error:x(d.projectId)});return}let y=d.sessionId||(0,Vn.randomUUID)(),_=!d.sessionId,N=d.sessionId?Ms(d.sessionId):null,U=N?.agentId||d.agentId||g.project.defaultAgent||xe.DEFAULT_AGENT_ID;if(!_&&d.userId){let qe=N?.ownerUserId??"";if(qe&&qe!==d.userId){S.sendChatTurnResult(d.requestId,{ok:!1,sessionId:y,reply:"",error:"this chat belongs to someone else - you can read it, but only its owner can continue it. Start your own chat for this."});return}}let G=d.operator===!0;if(G&&!t.operatorEnabled){S.sendChatTurnResult(d.requestId,{ok:!1,sessionId:y,reply:"",error:"operator mode is not enabled on this machine. Whoever administers the server must run `allwright operator on` there once (and `allwright operator off` turns it back off) - a dashboard permission alone is deliberately not enough."});return}let H=_?0:N?.messages.length??0,C=!1,z=0,ee="";if(!_&&H>=aw){let qe=await uw(g,y,t);if(qe)C=!0,z=H,ee=qe,y=(0,Vn.randomUUID)(),_=!0,b.info(`chat compacted: ${H} message(s) summarized into a fresh session`);else if(H>=cw){S.sendChatTurnResult(d.requestId,{ok:!1,sessionId:y,reply:"",error:"this chat is very long and could not be compacted automatically - start a new chat (it keeps the repo knowledge; only the conversation resets)."});return}}let M=d.files.length?wg(g.repoPath,d.requestId,d.files):null,te=(0,xe.replyPreferenceAppendix)({language:d.replyLanguage,languageInstruction:d.replyLanguageInstruction,style:d.replyStyle})+(0,xe.pendingMigrationsAppendix)(d.pendingMigrations)+(0,xe.monitorChecksAppendix)(d.monitorChecks)+(0,xe.deployTargetsAppendix)(d.environments)+(0,xe.savedStepChecksAppendix)(d.environmentSteps)+(0,xe.composeCommandAppendix)(d.command)+ow(d.sessionId??""),ae=XC(g.repoPath)+dr("compose",g.repoPath,{summary:d.message,ruleName:""});ec(g,t);let pe="```",$e=_?`You are the project assistant for "${g.project.name}" - this working directory is its repository, and you have read-only access.
1731
+ - "chains": tickets one engineer would do in one sitting, in the same part of the code, ADJACENT in your order. A chained ticket gets a short handoff note from the one before it (not the full prior work) - group real neighbours, and leave a ticket out of every chain when unsure. Solo tickets appear in no chain.
1732
+ - "dependencies": ONLY hard prerequisites, where the blocked ticket CANNOT pass if the other failed. An empty list is the normal answer.`,G=await N.adapter.run({repoPath:g.repoPath,prompt:U,timeoutSeconds:90,bin:N.bin,mode:"read_only",maxTurns:3,model:N.adapter.fastModel,sessionId:(0,Vn.randomUUID)(),onLog:()=>{}});_e({surface:"autopilot_plan",projectId:g.project.id,agentId:N.adapter.id,refKind:"none",refLabel:`${d.issueKeys.length} tickets`,...ve(G)});let H=th(G.resultText,d.issueKeys);if(!H){S.sendAutopilotPlanResult(d.requestId,{ok:!1,error:"the planner's answer could not be parsed - the queue keeps its given order"});return}let{saveBatchPlan:C}=await Promise.resolve().then(()=>(Nc(),Bb));C({batchId:d.batchId,projectId:d.projectId,order:H.order,chains:H.chains,dependencies:H.dependencies,reasoning:H.reasoning}),S.sendAutopilotPlanResult(d.requestId,{ok:!0,...H})}catch(g){S.sendAutopilotPlanResult(d.requestId,{ok:!1,error:g instanceof Error?g.message:String(g)})}},onChatDiagnostic:async d=>{try{let{runDiagnostic:g}=await Promise.resolve().then(()=>(Uh(),Lv)),y=await g({command:d.command,args:d.args});b.info(`chat diagnostic (user-approved): ${y.commandLine}`),S.sendChatDiagnosticResult(d.requestId,y)}catch(g){S.sendChatDiagnosticResult(d.requestId,{ok:!1,output:g instanceof Error?g.message:String(g)})}},onChatStepRun:async d=>{let g=D.lookup(d.projectId,d.environmentName);if(!g){S.sendChatStepRunResult(d.requestId,{ok:!1,error:`this runner has no environment "${d.environmentName}" for that project`});return}let{environment:y}=g,_=y.steps[d.stepIndex];if(!_||_.name!==d.stepName){S.sendChatStepRunResult(d.requestId,{ok:!1,error:`step ${d.stepIndex+1} of "${y.name}" is ${_?`"${_.name}"`:"not there"} on this machine, not "${d.stepName}" - the recipe changed since the chat read it. Reopen the environment and ask again.`});return}let N=`${d.projectId}:${y.name}:${d.stepIndex}`;if(P.has(N)){S.sendChatStepRunResult(d.requestId,{ok:!1,stepName:_.name,error:"that step is already running here - wait for it to finish"});return}P.add(N),b.info(`chat step re-run (user-approved): [${y.name}] ${_.name}`);try{let{runSavedStep:U}=await Promise.resolve().then(()=>(Is(),xy)),G=await U({repoPath:g.repoPath,repoName:g.project.repoName,environmentName:y.name,workdir:y.workdir,branch:y.branch,step:_,cfg:t,timeoutMs:Math.min(y.timeoutSeconds*1e3,YC)});S.sendChatStepRunResult(d.requestId,{ok:!0,stepName:_.name,...G})}catch(U){S.sendChatStepRunResult(d.requestId,{ok:!1,stepName:_.name,error:U instanceof Error?U.message:String(U)})}finally{P.delete(N)}},onChatCancel:d=>{let g=L.get(d.turnId);g&&(g.abort(),b.info(`chat turn ${d.turnId} stopped from the dashboard`))},onChatTurn:async d=>{let g=A(d.projectId);if(!g){S.sendChatTurnResult(d.requestId,{ok:!1,sessionId:d.sessionId??"",reply:"",error:x(d.projectId)});return}let y=d.sessionId||(0,Vn.randomUUID)(),_=!d.sessionId,N=d.sessionId?Ms(d.sessionId):null,U=N?.agentId||d.agentId||g.project.defaultAgent||xe.DEFAULT_AGENT_ID;if(!_&&d.userId){let qe=N?.ownerUserId??"";if(qe&&qe!==d.userId){S.sendChatTurnResult(d.requestId,{ok:!1,sessionId:y,reply:"",error:"this chat belongs to someone else - you can read it, but only its owner can continue it. Start your own chat for this."});return}}let G=d.operator===!0;if(G&&!t.operatorEnabled){S.sendChatTurnResult(d.requestId,{ok:!1,sessionId:y,reply:"",error:"operator mode is not enabled on this machine. Whoever administers the server must run `allwright operator on` there once (and `allwright operator off` turns it back off) - a dashboard permission alone is deliberately not enough."});return}let H=_?0:N?.messages.length??0,C=!1,z=0,ee="";if(!_&&H>=aw){let qe=await uw(g,y,t);if(qe)C=!0,z=H,ee=qe,y=(0,Vn.randomUUID)(),_=!0,b.info(`chat compacted: ${H} message(s) summarized into a fresh session`);else if(H>=cw){S.sendChatTurnResult(d.requestId,{ok:!1,sessionId:y,reply:"",error:"this chat is very long and could not be compacted automatically - start a new chat (it keeps the repo knowledge; only the conversation resets)."});return}}let M=d.files.length?wg(g.repoPath,d.requestId,d.files):null,te=(0,xe.replyPreferenceAppendix)({language:d.replyLanguage,languageInstruction:d.replyLanguageInstruction,style:d.replyStyle})+(0,xe.pendingMigrationsAppendix)(d.pendingMigrations)+(0,xe.monitorChecksAppendix)(d.monitorChecks)+(0,xe.deployTargetsAppendix)(d.environments)+(0,xe.savedStepChecksAppendix)(d.environmentSteps)+(0,xe.composeCommandAppendix)(d.command)+ow(d.sessionId??""),ae=XC(g.repoPath)+dr("compose",g.repoPath,{summary:d.message,ruleName:""});ec(g,t);let pe="```",$e=_?`You are the project assistant for "${g.project.name}" - this working directory is its repository, and you have read-only access.
1733
1733
 
1734
1734
  DEFAULT MODE IS DISCUSSION. Answer questions, explain code, weigh approaches, review ideas - like a teammate who knows this repo. Most conversations never produce a ticket, and that is fine. Do NOT propose a Jira ticket unless the user explicitly asks for one in this conversation ("create a ticket", "make this a ticket", "draft the ticket" or similar). Never end a normal answer with a ticket proposal.
1735
1735
  Be fast: answer from CLAUDE.md and what you already know first; open files only when the question genuinely needs code-level detail. Keep replies short.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "allwright",
3
- "version": "1.133.0",
3
+ "version": "1.134.0",
4
4
  "description": "Allwright — the AI teammate that runs on YOUR machine. Polls your Jira, runs jailed agents in your local checkouts, opens PRs, executes approved deploys, and audits security/testing/architecture. Credentials and code never leave your host.",
5
5
  "bin": {
6
6
  "allwright": "bin/allwright.js",