@agentdeploymentco/bishop 0.6.0 → 0.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.
- package/dist/cli.js +11 -11
- package/dist/config.js +4 -4
- package/dist/harness/claude-options.js +1 -1
- package/dist/harness/claude-settings.js +1 -0
- package/dist/harness/claude.js +6 -6
- package/dist/harness/codex-project.js +2 -0
- package/dist/harness/codex.js +4 -4
- package/dist/harness/create.js +1 -1
- package/dist/harness/layers.js +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{stat as me}from"node:fs/promises";import{join as le,resolve as he}from"node:path";import{Command as ue}from"commander";import{agentSnapshots as fe,fixedAgentDir as we}from"./agent-dir.js";import{looksRemote as
|
|
3
|
-
`),process.exit(1)}async function Dt(e){const t=he(e??".");let n;try{n=(await me(t)).isDirectory()}catch{throw new v(`--agent directory doesn't exist: ${t}`)}if(!n)throw new v(`--agent is not a directory: ${t}`);return t}async function Mt(e){const t=await Ct(e),n={enabled:!0,isRepo:t};return t?(
|
|
2
|
+
import{stat as me}from"node:fs/promises";import{join as le,resolve as he}from"node:path";import{Command as ue}from"commander";import{agentSnapshots as fe,fixedAgentDir as we}from"./agent-dir.js";import{looksRemote as q,parseRepoSpec as Y}from"./agent-source.js";import{everyStore as W,storePath as J}from"./agent-store.js";import{attachmentStore as ge}from"./attachments.js";import{HARNESSES as ye,loadConfig as $,nonEmptyFlag as Q,parseHarnessName as Se,positiveIntFlag as ke,resolveAgent as be,sandboxConfig as ve,writeConfig as Te}from"./config.js";import{dbPath as V,openDb as X}from"./db.js";import{describeMissing as Ie,loadEnv as b,readCredentials as Ee}from"./env.js";import{BishopError as v}from"./errors.js";import{fileStore as xe}from"./files.js";import{DEFAULT_MAX_AGE_DAYS as Ae,makeCollector as _e,sweep as Re}from"./gc.js";import{ghAuthLogin as Ne,ghCliIn as Oe,gitAuthenticatesToGithub as Ge,withTemporaryGhConfig as $e}from"./github/gh.js";import{AGENT_SCOPES as Le,checkGithubSetup as Ce}from"./github/setup.js";import{createGmail as He}from"./gmail/create.js";import{checkGmailSetup as Pe}from"./gmail/setup.js";import{createHarness as De}from"./harness/create.js";import{sharedFileFetcher as Me}from"./interface/attachments.js";import{principalCheck as Z}from"./interface/authorize.js";import{deliveryRouter as Be}from"./interface/delivery.js";import{conversationMatch as Ue,destinationResolver as Fe,directDestination as Ke}from"./interface/destination.js";import{messageReader as ze}from"./interface/messages.js";import{peopleFinder as je}from"./interface/people.js";import{log as d,requestedLevel as qe}from"./log.js";import{DEFAULT_PROFILE as Ye,setupNono as We}from"./nono-setup.js";import{principalStore as Je}from"./principals.js";import{startReviewer as Qe}from"./review.js";import{checkSandbox as Ve,clearedStaging as Xe,sandboxPolicy as Ze,writeLauncher as et}from"./sandbox.js";import{addSchedule as tt,listSchedules as ot,removeSchedule as nt,setScheduleEnabled as ee}from"./schedule/cli.js";import{authorizedToRun as st,makeScheduler as it}from"./schedule/runner.js";import{scheduleStore as rt}from"./schedule/store.js";import{threadQueue as at}from"./sessions/queue.js";import{interfaceState as ct,retentionStore as te,sessionStore as pt,threadContexts as dt,threadHandover as mt,threadMeta as lt}from"./sessions/store.js";import{ensureGitignore as ht,openBrowser as ut,resolveAppDetails as ft,SetupError as oe,setupSlack as wt,setupSlackManually as gt,withPrompt as ne,writeEnv as se}from"./setup.js";import{createSlackInterface as yt}from"./slack/interface.js";import{buildManifest as St}from"./slack/manifest.js";import{createTeams as kt}from"./teams/create.js";import{administratorInstructions as bt,repointTeamsEndpoint as vt,setupTeams as Tt,setupTeamsManually as It}from"./teams/setup.js";import{threadModel as Et}from"./thread-model.js";import{fileTools as xt}from"./tools/files.js";import{messageTools as At}from"./tools/messages.js";import{peopleTools as _t}from"./tools/people.js";import{scheduleTools as Rt}from"./tools/schedules.js";import{startToolServer as Nt}from"./tools/server.js";import{transcriptStore as Ot}from"./transcript.js";import{makeTurnRunner as Gt}from"./turn.js";import{version as $t}from"./version.js";import{hasGitIdentity as Lt,isGitRepo as Ct,sharedDirNotice as Ht,WORKTREE_DIR as Pt}from"./worktrees.js";function f(e){process.stderr.write(`${e}
|
|
3
|
+
`),process.exit(1)}async function Dt(e){const t=he(e??".");let n;try{n=(await me(t)).isDirectory()}catch{throw new v(`--agent directory doesn't exist: ${t}`)}if(!n)throw new v(`--agent is not a directory: ${t}`);return t}async function Mt(e){const t=await Ct(e),n={enabled:!0,isRepo:t};return t?(d.info({agentDir:e,worktrees:le(e,Pt)},"worktree mode is on"),await Lt(e)||d.warn({agentDir:e},"git has no user.name or user.email here, so the agent cannot commit what it does in a worktree"),n):(d.warn({agentDir:e},"worktree mode is on but the agent directory is not a git repository; threads share it"),n)}function Bt(e){if(e.kind==="options-replaced"){d.warn({harness:e.harness},`agent.harnesses.${e.harness} replaces agent.options, which is ignored for this run`);return}d.warn({harness:e.harness,configured:e.configured},`--harness overrides the configured harness, but agent.options is still written for that one; move it to agent.harnesses.${e.configured} and give ${e.harness} its own block`)}async function Ut(e=[],t={}){e.length>0&&f(`bishop: unknown command "${e[0]}"
|
|
4
4
|
|
|
5
|
-
Run \`bishop --help\` to see what's available.`);const n=process.cwd(),c=t.agent&&
|
|
5
|
+
Run \`bishop --help\` to see what's available.`);const n=process.cwd(),c=t.agent&&q(t.agent)?Y(t.agent):void 0;t.autoUpdate&&!c&&f("--auto-update needs --agent to be a git URL. Bishop never updates a directory it was pointed at; that state is yours to manage."),b(n);const s=await $(n),i=Ee();i.ok||f(Ie(i));const r=be(s,t);for(const o of r.warnings)Bt(o);const l=ve(s);if(l){const o=await Ve(l);d.info({nono:o,...l.profile?{profile:l.profile}:{}},"sandbox is on"),l.profile||d.warn("sandbox is enabled but agent.sandbox.profile is missing; run `bishop nono setup`")}const u=s.review?await Qe(s.review,{home:n}):void 0;u&&d.warn("review is configured, but this version only checks the reviewer at startup; no message is reviewed yet (#254)");const m=l?(o,p)=>Ze({home:n,cwd:o,...p?{dirKey:p}:{},...l.profile?{profile:l.profile}:{},...u?.unwritable.length?{unwritable:u.unwritable}:{}}):void 0,a=X(V(n)),h=pt(a,r.harness);let A;const re=async o=>await A?.check(o,await m?.(o))??{ok:!0},_=c?fe({spec:c,store:J(n,c.slug),dirs:h,autoUpdate:!!t.autoUpdate,recheck:re}):void 0,y=_?await _.start():await Dt(t.agent),ae=_?_.forTurn:we(y,h);d.info({bishopDir:n,agentDir:y,...y===n?{}:{separate:!0},...c?{source:c.cloneUrl,autoUpdate:!!t.autoUpdate}:{},app:s.slack?.app?.name},"bishop starting");const R=s.agent?.worktrees?await Mt(y):void 0,T=De(r.harness,{...r.options?{options:r.options}:{},...r.model?{model:r.model}:{},...r.effort?{effort:r.effort}:{},...R?{worktrees:R}:{},...s.agent?.projectSettings===!1?{projectSettings:!1}:{},...l?{launcher:await et(n)}:{},credentials:i.credentials}),N=await T.check(y,await m?.(y));N.ok||f(T.describeStartupFailure(y,N.error));const L=N.model??r.model;d.info({harness:T.name,credentials:N.credentialSource??"unreported",model:L,...r.effort?{effort:r.effort}:{},...r.options?{agentOptions:Object.keys(r.options)}:{}},"harness ready"),A=T,d.info({sessions:h.count()},"opened session store");const P=_e({retention:te(a),stores:()=>W(n),home:n,...s.gc?.maxAgeDays===void 0?{}:{maxAgeDays:s.gc.maxAgeDays}});P.start();let w;const ce=o=>w?Z(w,k)(o):st(o),pe=async(o,p)=>{if(!w)throw new v("no interface is running, so there is nowhere to send");return Fe(w,{optedInOnly:!0})(o,p)},de=(o,p)=>w?Ue(w)(o,p):!1,D=xe({home:n,...s.files?.maxMb===void 0?{}:{maxMb:s.files.maxMb}}),M=ge(a),k=Je(a),B=rt(a),U=await Nt({tools:[...Rt({schedules:B,authorized:ce,resolve:pe,sameConversation:de,principals:k}),...xt({fetch:Me({interfaces:()=>w??[],store:M,files:D})}),...i.credentials.slack?At({read:ze({interfaces:()=>w??[]})}):[],...i.credentials.teams?_t({find:je({interfaces:()=>w??[]})}):[]]}),O=lt(a),C=Et(O),F={...L?{model:L}:{},...r.effort?{effort:r.effort}:{}},K=Gt({harness:T,cwd:ae,sessions:h,model:C,tools:U,contexts:dt(a,O),transcript:Ot(a),principals:k,...R&&!R.isRepo?{sharedDirNotice:Ht(y)}:{},...m?{sandbox:{policy:(o,p)=>m(o,p.dirKey),staging:await Xe(n)}}:{}}),z=at(),I={threads:h,events:h,files:D,attachments:M,principals:k,run:o=>z.run(o.thread.threadKey,()=>K.run(o)),stop:o=>K.stop(o)},g=[];if(i.credentials.slack&&g.push(await yt({...I,credentials:i.credentials.slack,meta:O,model:C,configuredModel:F,...s.slack?.verbosity?{verbosity:s.slack.verbosity}:{},...s.slack?.allow?{allow:s.slack.allow}:{},...s.slack?.maxBotTurns===void 0?{}:{maxBotTurns:s.slack.maxBotTurns}})),i.credentials.gmail)try{g.push(await He({...I,credentials:i.credentials.gmail,...s.gmail?{config:s.gmail}:{},state:ct(a)}))}catch(o){o instanceof v&&f(o.message);const p=i.credentials.gmail,G=p.kind==="oauth"?"the authorized Gmail account":`${p.user}`;f(`Couldn't reach the mailbox for ${G}:
|
|
6
6
|
${o instanceof Error?o.message:String(o)}
|
|
7
7
|
|
|
8
|
-
Run \`bishop gmail setup\` to check the credentials.`)}if(
|
|
8
|
+
Run \`bishop gmail setup\` to check the credentials.`)}if(i.credentials.teams)try{g.push(await kt({...I,credentials:i.credentials.teams,meta:O,model:C,configuredModel:F,...s.teams?{config:s.teams}:{}}))}catch(o){o instanceof v&&f(o.message),f(`Couldn't start the Teams app for client ${i.credentials.teams.clientId}:
|
|
9
9
|
${o instanceof Error?o.message:String(o)}
|
|
10
10
|
|
|
11
|
-
Check the credentials in .env and the \`teams\` block in .bishop/config.json.`)}w=g;for(const o of g)await o.start();
|
|
11
|
+
Check the credentials in .env and the \`teams\` block in .bishop/config.json.`)}w=g;for(const o of g)await o.start();d.info({interfaces:g.map(o=>o.name)},"bishop is listening");const j=it({schedules:B,run:I.run,threads:h,cancel:I.stop,deliver:Be(g),authorized:Z(g,k),reach:Ke(g,k),principals:k,attach:mt(h)});j.start();for(const o of["SIGINT","SIGTERM"])process.once(o,()=>{d.info({signal:o,inFlight:z.size},"shutting down"),j.stop().catch(p=>d.error({err:p},"the scheduler failed to stop cleanly")).then(()=>Promise.allSettled(g.map(p=>p.stop()))).then(async p=>{const G=p.filter(E=>E.status==="rejected");for(const E of G)d.error({err:E.reason},"an interface failed to stop cleanly");await U.stop().catch(E=>{d.error({err:E},"the tool endpoint failed to stop cleanly")}),await P.stop(),a.close(),process.exit(G.length>0?1:0)})})}function Ft(e){for(const t of e.snapshots)process.stdout.write(`removed snapshot ${t.commit} (unused for ${t.ageDays} days)
|
|
12
12
|
`);for(const t of e.worktrees){const n=t.branch?`, branch ${t.branch}`:"";process.stdout.write(`released ${t.path}${n}
|
|
13
13
|
`)}process.stdout.write(`snapshots: removed ${e.snapshots.length}, kept ${e.keptSnapshots}
|
|
14
14
|
worktrees: released ${e.worktrees.length}
|
|
15
15
|
files: removed ${e.files} download directories
|
|
16
16
|
rows: forgot ${e.threads} threads, ${e.events} events
|
|
17
|
-
`)}async function Kt(e){const t=process.cwd();b(t),e.agent&&!
|
|
17
|
+
`)}async function Kt(e){const t=process.cwd();b(t),e.agent&&!q(e.agent)&&f("gc --agent takes the git URL Bishop cloned, not a path. It narrows the pass to the files Bishop owns for one agent, and it owns none for a directory agent. Run `bishop gc` with no --agent to collect what its threads left behind.");const n=e.agent?[J(t,Y(e.agent).slug)]:await W(t),c=await $(t),s=e.maxAgeDays??c.gc?.maxAgeDays;qe()||(d.level="error");const i=X(V(t));try{Ft(await Re({retention:te(i),stores:n,home:t,...e.agent?{only:n}:{},...s===void 0?{}:{maxAgeDays:s}}))}finally{i.close()}}function zt(){return!(process.env.SSH_CONNECTION||process.env.SSH_TTY||process.env.SSH_CLIENT||process.platform==="linux"&&!process.env.DISPLAY&&!process.env.WAYLAND_DISPLAY)}async function jt(e={}){const t=process.cwd();b(t);const n=process.env.BISHOP_GMAIL_CLIENT_ID?.trim(),c=process.env.BISHOP_GMAIL_CLIENT_SECRET?.trim(),s=process.env.BISHOP_GMAIL_REFRESH_TOKEN?.trim(),i=process.env.BISHOP_GMAIL_USER?.trim(),r=process.env.BISHOP_GMAIL_SERVICE_ACCOUNT?.trim();n&&!c&&f("BISHOP_GMAIL_CLIENT_ID is set but BISHOP_GMAIL_CLIENT_SECRET is not.");const l=e.browser===!1||!zt()?"paste":"browser",u=await ne(!0,async m=>Pe({...n&&c?{client:{clientId:n,clientSecret:c}}:{},...s?{refreshToken:s}:{},...i?{user:i}:{},...r?{serviceAccount:r}:{},mode:l,openBrowser:ut,readPaste:async a=>(process.stdout.write(`Open this in a browser and approve it:
|
|
18
18
|
|
|
19
19
|
${a}
|
|
20
20
|
|
|
@@ -22,14 +22,14 @@ It will finish on a http://127.0.0.1 address that fails to load. That is expecte
|
|
|
22
22
|
Copy the whole address out of the browser and paste it here.
|
|
23
23
|
|
|
24
24
|
`),m("Redirect URL",""))}));if(process.stdout.write(`${u.report}
|
|
25
|
-
`),u.refreshToken){const m=await
|
|
25
|
+
`),u.refreshToken){const m=await se(t,{BISHOP_GMAIL_REFRESH_TOKEN:u.refreshToken});process.stdout.write(`
|
|
26
26
|
Saved BISHOP_GMAIL_REFRESH_TOKEN to ${m}
|
|
27
|
-
`)}u.ok||process.exit(1)}async function
|
|
27
|
+
`)}u.ok||process.exit(1)}async function qt(e){const t=process.cwd(),n=process.env.GH_TOKEN?.trim();process.env.GH_TOKEN!==void 0&&!n&&delete process.env.GH_TOKEN,b(t);const c=process.env.GH_TOKEN?.trim(),s=!!c&&!n,i=e.prompt&&process.stdin.isTTY===!0,r=e.print?process.stderr:process.stdout,l=a=>r.write(a),u=a=>Ce({...c?{token:c,tokenIsSaved:s}:{},...i?{login:()=>Ne(Le.map(h=>h.scope),a),confirm:async h=>/^y/i.test(await ne(!0,A=>A(h,"y"),r))}:{},gitConfigured:Ge,gh:Oe(a),...e.temporary?{temporary:!0}:{}}),m=e.temporary&&!c?await $e(u):await u();if(l(`${m.report}
|
|
28
28
|
`),e.print)m.token&&process.stdout.write(`${JSON.stringify({GH_TOKEN:m.token})}
|
|
29
|
-
`);else if(m.token){if(m.source==="gh"){const h=await
|
|
29
|
+
`);else if(m.token){if(m.source==="gh"){const h=await se(t,{GH_TOKEN:m.token});l(`
|
|
30
30
|
Saved GH_TOKEN to ${h}
|
|
31
31
|
`)}const a=await ht(t,[".env"]);a.length>0&&l(`Added ${a.join(", ")} to .gitignore
|
|
32
32
|
`)}m.ok||process.exit(1)}async function Yt(e){const t=process.cwd(),n=await We({config:await $(t),name:e.name});process.stdout.write(`${n.report}
|
|
33
33
|
`),n.config&&process.stdout.write(`Saved ${await Te(n.config,t)}
|
|
34
|
-
`),n.ok||process.exit(1)}async function Wt(e){b(process.cwd());const t={...e.name===void 0?{}:{name:e.name},...e.displayName===void 0?{}:{displayName:e.displayName},...e.description===void 0?{}:{description:e.description},prompt:e.prompt};e.manual?await gt(t):await wt(t)}async function Jt(e){if(e.appId!==void 0&&!e.manual)throw new oe("--app-id adopts an app somebody else created, so it needs --manual too.");if(e.endpoint!==void 0&&e.manual)throw new oe("--endpoint sets the URL on an app Bishop is creating, so it cannot be used\nwith --manual. Point an adopted app with `bishop teams endpoint <url>`,\nor ask whoever created it to.");b(process.cwd());const t={...e.name===void 0?{}:{name:e.name},...e.displayName===void 0?{}:{displayName:e.displayName},...e.description===void 0?{}:{description:e.description},...e.endpoint===void 0?{}:{endpoint:e.endpoint},...e.appId===void 0?{}:{appId:e.appId},prompt:e.prompt};e.manual?await It(t):await Tt(t)}async function Qt(e){const t=process.cwd(),n=await $(t),c=await ft({name:e.name??n.slack?.app?.name,displayName:e.displayName??n.slack?.app?.displayName,description:e.description??n.slack?.app?.description,prompt:!1},t,async(i
|
|
35
|
-
`)}const
|
|
34
|
+
`),n.ok||process.exit(1)}async function Wt(e){b(process.cwd());const t={...e.name===void 0?{}:{name:e.name},...e.displayName===void 0?{}:{displayName:e.displayName},...e.description===void 0?{}:{description:e.description},prompt:e.prompt};e.manual?await gt(t):await wt(t)}async function Jt(e){if(e.appId!==void 0&&!e.manual)throw new oe("--app-id adopts an app somebody else created, so it needs --manual too.");if(e.endpoint!==void 0&&e.manual)throw new oe("--endpoint sets the URL on an app Bishop is creating, so it cannot be used\nwith --manual. Point an adopted app with `bishop teams endpoint <url>`,\nor ask whoever created it to.");b(process.cwd());const t={...e.name===void 0?{}:{name:e.name},...e.displayName===void 0?{}:{displayName:e.displayName},...e.description===void 0?{}:{description:e.description},...e.endpoint===void 0?{}:{endpoint:e.endpoint},...e.appId===void 0?{}:{appId:e.appId},prompt:e.prompt};e.manual?await It(t):await Tt(t)}async function Qt(e){const t=process.cwd(),n=await $(t),c=await ft({name:e.name??n.slack?.app?.name,displayName:e.displayName??n.slack?.app?.displayName,description:e.description??n.slack?.app?.description,prompt:!1},t,async(s,i)=>i);process.stdout.write(`${JSON.stringify(St(c),null,2)}
|
|
35
|
+
`)}const S=new ue().name("bishop").description("An agent gateway. Runs an agent and connects it to Slack, email, and Teams.").version($t,"-v, --version").enablePositionalOptions().option("--agent <path-or-url>","the agent's directory, or a git URL to clone; defaults to the current directory").option(`--harness <${ye.join("|")}>`,"which SDK runs the agent",Se).option("--model <model>","the model to run, in the harness's own naming",Q("--model")).option("--effort <effort>","how hard the model should think, in the harness's own naming",Q("--effort")).option("--auto-update","for a git-URL --agent, check for a new commit at the start of every new thread").allowExcessArguments(!0).action((e,t)=>Ut(t.args,e));S.command("gc").description("Collect the snapshots, worktrees, and rows old conversations left behind").option("--max-age-days <days>",`how long a quiet thread's remains are kept; defaults to gc.maxAgeDays, then ${Ae}`,ke("--max-age-days")).option("--agent <url>","only this agent's snapshots; defaults to every agent Bishop has cloned here").action(Kt);const x=S.command("schedules").description("Prompts the agent runs on a clock, and where their answers go");x.command("list").description("Everything scheduled here, in your own time zone").action(()=>ot(process.cwd())),x.command("add").description("Add a schedule, or with --at a reminder that runs once and is then deleted").requiredOption("--name <name>","what a person sees in a list, and the subject of an email").option("--cron <expression>","five-field cron, or @daily and friends").option("--at <time>","run once instead, at an ISO 8601 time like 2026-10-09T15:00").requiredOption("--prompt <text>","what the agent is asked, including when to stay quiet").requiredOption("--to <destination>","#channel or @person for Slack, an address for Gmail").option("--tz <zone>","IANA zone --cron or --at is read in; defaults to this machine").option("--interface <name>","which interface delivers, when --to is ambiguous").action(e=>tt(process.cwd(),e)),x.command("rm <id>").description("Remove a schedule").action(e=>nt(process.cwd(),e)),x.command("disable <id>").description("Stop a schedule firing, keeping it").action(e=>ee(process.cwd(),e,!1)),x.command("enable <id>").description("Start a disabled schedule firing again, from now").action(e=>ee(process.cwd(),e,!0));const Vt=S.command("gmail").description("Gmail interface commands");Vt.command("setup").description("Authorize a Gmail mailbox, or check the one already configured").option("--no-browser","print a URL to open elsewhere instead of opening one here").action(jt);const Xt=S.command("github").description("GitHub credentials for the agent");Xt.command("setup").description("Give the agent a GitHub token, or check the one it's using").option("--no-prompt","don't offer to run `gh auth login`").option("--print","write the credential to stdout as JSON instead of to .env").option("--temporary","log in without touching this machine's own gh login").action(qt);const Zt=S.command("nono").description("The nono sandbox the agent runs under");Zt.command("setup").description("Write a nono profile that lets a sandboxed agent push").option("--name <name>","the profile name",Ye).action(Yt);const ie=S.command("slack").description("Slack interface commands");ie.command("setup").description("Create and install a Slack app, and save its credentials here").option("--name <name>","app name; defaults to the current directory name").option("--display-name <name>","bot display name in Slack; defaults to --name").option("--description <text>","short description shown in Slack").option("--no-prompt","don't prompt for anything left off").option("--manual","adopt an app you created in Slack yourself, using its tokens").action(Wt),ie.command("manifest").description("Print the app manifest, for creating an app in Slack by hand").option("--name <name>","app name; defaults to config or the directory name").option("--display-name <name>","bot display name; defaults to --name").option("--description <text>","short description shown in Slack").action(Qt);const H=S.command("teams").description("Microsoft Teams interface commands");H.command("setup").description("Create a Teams app and save its credentials here, or check the one configured").option("--name <name>","app name; defaults to the current directory name").option("--display-name <name>","app name shown in Teams; defaults to --name").option("--description <text>","short description shown in Teams").option("--endpoint <url>","the URL Teams POSTs to; can be set later").option("--app-id <id>","with --manual, the Teams app ID you were given").option("--no-prompt","don't prompt for anything left off").option("--manual","adopt an app somebody else created, using its credentials").action(Jt),H.command("endpoint <url>").description("Point the Teams app at a different URL, changing nothing else").option("--app-id <id>","the Teams app to point; defaults to the one in config").action((e,t)=>vt(e,t.appId===void 0?{}:{appId:t.appId})),H.command("instructions").description("Print what to send whoever creates the Teams app, when that is an administrator").option("--endpoint <url>","the URL Teams should POST to, if you already know it").action(e=>{process.stdout.write(bt(e.endpoint))});try{await S.parseAsync()}catch(e){throw e instanceof v&&f(e.message),e}
|
package/dist/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{mkdir as
|
|
2
|
-
`)}async function K(n=process.cwd()){const t=
|
|
3
|
-
${G(i.error)}`,t);return i.data}async function Q(n,t=process.cwd()){const e=
|
|
4
|
-
`,"utf8"),e}function X(n){return n.displayName?.trim()||n.name}export{
|
|
1
|
+
import{mkdir as h,readFile as w,writeFile as x}from"node:fs/promises";import{dirname as O,join as y}from"node:path";import{z as o}from"zod";import{BishopError as p}from"./errors.js";const j=".bishop",v="config.json",E=o.strictObject({id:o.string().optional(),name:o.string().min(1),displayName:o.string().optional(),description:o.string().optional()}),k=["low","medium","high"],J="high",f=o.string().transform(n=>n.trim().toLowerCase()).pipe(o.enum(k)),$=o.strictObject({workspace:o.string().optional(),app:E.optional(),verbosity:f.optional(),allow:o.array(o.string().min(1)).optional(),maxBotTurns:o.number().int().nonnegative().optional()}),R=o.strictObject({allow:o.array(o.string().min(1)).optional(),pollSeconds:o.number().int().positive().optional(),labelPrefix:o.string().trim().min(1).optional(),markRead:o.boolean().optional(),internalDomains:o.array(o.string().trim().toLowerCase().regex(/^[^\s@]+\.[^\s@]+$/,"must be a domain like example.com")).optional()}),N=o.strictObject({cert:o.string().trim().min(1),key:o.string().trim().min(1)}),S=o.strictObject({id:o.string().optional(),name:o.string().min(1),displayName:o.string().optional(),description:o.string().optional()}),I=o.strictObject({app:S.optional(),allow:o.array(o.string().min(1)).optional(),verbosity:f.optional(),port:o.number().int().min(1).max(65535).optional(),host:o.string().trim().min(1).optional(),tls:N.optional()}),a=["claude","codex"],C=o.strictObject({model:o.string().trim().min(1).optional(),effort:o.string().trim().min(1).optional(),options:o.record(o.string(),o.unknown()).optional()}),A=o.strictObject({harness:o.enum(a).optional(),model:o.string().trim().min(1).optional(),effort:o.string().trim().min(1).optional(),harnesses:o.partialRecord(o.enum(a),C,{error:()=>`not a harness Bishop runs; supported: ${a.join(", ")}`}).optional(),worktrees:o.boolean().optional(),projectSettings:o.boolean().optional(),sandbox:o.union([o.boolean(),o.strictObject({profile:o.string().trim().min(1).optional()})]).optional(),options:o.record(o.string(),o.unknown()).optional()}),F=o.strictObject({maxAgeDays:o.number().int().positive().optional()}),T=o.strictObject({maxMb:o.number().positive().optional()}),_=["all","external"],L=["reject","approve"],g=o.strictObject({scope:o.enum(_).optional(),onError:o.enum(L).optional()});function B(n){let t;try{t=new URL(n)}catch{return!1}return t.protocol==="https:"?!0:t.protocol!=="http:"?!1:t.hostname==="localhost"||t.hostname==="[::1]"||/^127(\.\d{1,3}){3}$/.test(t.hostname)}const D=o.strictObject({command:o.array(o.string().min(1)).min(1).optional(),url:o.string().trim().refine(B,"must be an https URL, or http on loopback").optional(),inbound:g.optional(),outbound:g.optional(),timeoutSeconds:o.number().positive().max(300).optional(),env:o.array(o.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/,"must be an environment variable name")).optional()}).superRefine((n,t)=>{n.command&&n.url?t.addIssue({code:"custom",message:"names both command and url; pick one"}):!n.command&&!n.url&&t.addIssue({code:"custom",message:"needs a command or a url"}),n.url&&n.env&&t.addIssue({code:"custom",path:["env"],message:"is only for a command reviewer"}),!n.inbound&&!n.outbound&&t.addIssue({code:"custom",message:"reviews nothing without inbound or outbound"})}),V=o.strictObject({slack:$.optional(),gmail:R.optional(),teams:I.optional(),agent:A.optional(),files:T.optional(),gc:F.optional(),review:D.optional()}).superRefine((n,t)=>{n.review&&!n.agent?.sandbox&&t.addIssue({code:"custom",path:["review"],message:"needs agent.sandbox on"})});class l extends p{path;constructor(t,e){super(t,"ConfigError"),this.path=e}}function W(n,t={}){const e=n.agent,s=e?.harness??"claude",i=t.harness??s,r=e?.harnesses?.[i],c=[];e?.options&&(r&&i===s?c.push({kind:"options-replaced",harness:i}):!r&&i!==s&&c.push({kind:"options-foreign",harness:i,configured:s}));const m=t.model??r?.model??e?.model,u=t.effort??r?.effort??e?.effort,d=r?r.options:e?.options;return{harness:i,...m?{model:m}:{},...u?{effort:u}:{},...d?{options:d}:{},warnings:c}}function Y(n){const t=n.agent?.sandbox;if(t)return t===!0||!t.profile?{}:{profile:t.profile}}function Z(n){return t=>{const e=t.trim();if(!e)throw new p(`${n} needs a value`);return e}}function M(n){return t=>{const e=Number(t.trim());if(!Number.isInteger(e)||e<=0)throw new p(`${n} needs a positive whole number, got "${t}"`);return e}}function q(n){const t=n.trim();if(a.includes(t))return t;throw new p(`unknown harness "${n}"; supported: ${a.join(", ")}`)}function b(n=process.cwd()){return y(n,j,v)}function G(n){return n.issues.map(t=>`${t.path.length>0?t.path.join("."):"(root)"}: ${t.message}`).join(`
|
|
2
|
+
`)}async function K(n=process.cwd()){const t=b(n);let e;try{e=await w(t,"utf8")}catch(r){if(r.code==="ENOENT")return{};throw new l(`Couldn't read ${t}: ${r.message}`,t)}let s;try{s=JSON.parse(e)}catch(r){throw new l(`${t} isn't valid JSON: ${r.message}`,t)}const i=V.safeParse(s);if(!i.success)throw new l(`${t} has invalid values:
|
|
3
|
+
${G(i.error)}`,t);return i.data}async function Q(n,t=process.cwd()){const e=b(t);return await h(O(e),{recursive:!0}),await x(e,`${JSON.stringify(n,null,2)}
|
|
4
|
+
`,"utf8"),e}function X(n){return n.displayName?.trim()||n.name}export{j as CONFIG_DIR,v as CONFIG_FILE,l as ConfigError,V as ConfigSchema,J as DEFAULT_VERBOSITY,a as HARNESSES,L as REVIEW_ON_ERROR,_ as REVIEW_SCOPES,k as VERBOSITY,b as configPath,X as displayNameOf,K as loadConfig,Z as nonEmptyFlag,q as parseHarnessName,M as positiveIntFlag,W as resolveAgent,Y as sandboxConfig,Q as writeConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SERVER_NAME as
|
|
1
|
+
import{SERVER_NAME as l}from"../tools/server.js";import{mergeSettings as a}from"./claude-settings.js";import{layerOptions as c,mergeEnv as i,PLUGIN_INSTALL_TIMEOUT_MS as m}from"./layers.js";const f={permissionMode:"auto"},y=["autoMemoryEnabled"],d=["cwd","resume","forkSession","sessionId","systemPrompt","abortController","canUseTool"];function g(e,o){const r=e??void 0;return o?{...r,[l]:{type:"http",url:o.url,headers:{Authorization:`Bearer \${${o.tokenEnvVar}}`},alwaysLoad:!0}}:r}function S(e,o){const r=e??void 0;if(!o)return r;const t={...r};for(const[s,n]of Object.entries(o))t[s]=[...n,...r?.[s]??[]];return t}function E(e,o,r){if(typeof e=="string")return e;const t={autoMemoryEnabled:!1};return r&&(t.worktree={baseRef:"head"}),a(t,o,e)}function N(e){const o=c({defaults:f,...e.configOptions?{configOptions:e.configOptions}:{},...e.general?{general:e.general}:{},controlledKeys:d}),r=S(o.hooks,e.hooks);r&&(o.hooks=r);const t=g(o.mcpServers,e.tools);t&&(o.mcpServers=t),o.settings=E(o.settings,e.agentSettings,e.worktrees===!0),o.permissionMode==="bypassPermissions"&&(o.allowDangerouslySkipPermissions=!0);const s=i(o.env,{...e.apiKey?{ANTHROPIC_API_KEY:e.apiKey}:{},...e.tools?{[e.tools.tokenEnvVar]:e.tools.token}:{},...e.installPlugins?{CLAUDE_CODE_SYNC_PLUGIN_INSTALL:"1",CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS:String(m)}:{}});return s&&(o.env=s),o.cwd=e.controlled.cwd,o.systemPrompt=e.controlled.systemPrompt,o.abortController=e.controlled.abortController,e.controlled.resume&&(o.resume=e.controlled.resume),o}export{d as CONTROLLED_KEYS,f as DEFAULT_AGENT_OPTIONS,y as SETTINGS_ONLY_KEYS,N as buildAgentOptions,g as mergeMcpServers};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{realpath as d,stat as p}from"node:fs/promises";import{join as u}from"node:path";import{resolveSettings as m}from"@anthropic-ai/claude-agent-sdk";import{log as s}from"../log.js";import{sandboxReadable as g}from"../sandbox.js";import{isPlainObject as l,PLUGIN_INSTALL_TIMEOUT_MS as h,unionLists as w}from"./layers.js";const S=1e3,j=u(".claude","settings.json");async function L(e,n){const r=u(e,j),i=await p(r).catch(()=>{});if(i)try{const t=await d(r);if(!i.isFile()||n&&!g(n,t)){s.warn({path:r,resolved:t},"the agent's settings file isn't a file the agent could read itself; passing nothing from it");return}const o=(await m({cwd:e,settingSources:["project"]})).sources.find(f=>f.source==="project");if(o)return o.settings;s.warn({path:r},"the agent's settings file isn't valid Claude settings, so Claude skips it and Bishop passes nothing from it")}catch(t){s.warn({path:r,err:t},"couldn't read the agent's settings file; passing nothing from it")}}function c(e,n){if(n===void 0)return e;if(Array.isArray(e)&&Array.isArray(n))return w(e,n);if(l(e)&&l(n)){const r=new Set([...Object.keys(e),...Object.keys(n)]),i=(t,a)=>Object.hasOwn(t,a)?t[a]:void 0;return Object.fromEntries([...r].map(t=>[t,c(i(e,t),i(n,t))]))}return n}function k(...e){return e.reduce((n,r)=>c(n,r),{})}function E(e,n=h,r=Date.now){let i;return t=>{if(!(t.type!=="system"||t.subtype!=="plugin_install"))switch(t.status){case"started":i=r();return;case"installed":s.info({...e,marketplace:t.name},"installed plugins from a marketplace");return;case"failed":s.warn({...e,marketplace:t.name,error:t.error},"plugins from a marketplace failed to install");return;case"completed":{if(i===void 0||r()-i<n-S)return;s.warn({...e,timeoutMs:n},"plugin install hit its time limit; this turn runs without whatever had not finished");return}}}}export{j as SETTINGS_FILE,k as mergeSettings,E as pluginInstallWatcher,L as readAgentSettings};
|
package/dist/harness/claude.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import{spawn as
|
|
2
|
-
`)[0]??"";return
|
|
3
|
-
`)}function
|
|
1
|
+
import{spawn as D}from"node:child_process";import{existsSync as K,mkdirSync as j}from"node:fs";import{mkdir as F}from"node:fs/promises";import{homedir as L}from"node:os";import{join as S,resolve as k}from"node:path";import{query as I}from"@anthropic-ai/claude-agent-sdk";import{log as h}from"../log.js";import{executableDir as N,nonoCommand as P}from"../sandbox.js";import{isolates as x,repoRoot as $,WORKTREE_DIR as G,worktreePath as U}from"../worktrees.js";import{buildAgentOptions as W,CONTROLLED_KEYS as B,SETTINGS_ONLY_KEYS as H}from"./claude-options.js";import{pluginInstallWatcher as Y,readAgentSettings as M}from"./claude-settings.js";import{ignoredKeys as A,shadowedKeys as z}from"./layers.js";import{buildPrompt as J,principalInstructions as Q}from"./principal.js";function*V(e){switch(e.type){case"system":e.subtype==="init"&&(yield{type:"session",sessionId:e.session_id,...e.model?{model:e.model}:{}});return;case"assistant":for(const t of e.message.content)if(t.type==="text"&&t.text.trim())yield{type:"text",text:t.text};else if(t.type==="tool_use"){const r=X(t.input);yield{type:"tool",name:t.name,...r?{detail:r}:{}}}else t.type==="thinking"&&(yield{type:"thinking"});return;case"tool_use_summary":yield{type:"tool_summary",summary:e.summary};return;case"result":e.subtype==="success"?yield{type:"done",text:e.result,usage:{turns:e.num_turns,costUsd:e.total_cost_usd}}:yield{type:"error",kind:e.subtype,message:O(e.subtype)};return;default:return}}function X(e){if(!e||typeof e!="object")return;const t=e,r=t.file_path??t.notebook_path??t.path;if(typeof r=="string"&&r)return r.split("/").pop()??r;const o=t.url;if(typeof o=="string"&&o)try{return new URL(o).hostname}catch{return o.slice(0,40)}for(const a of["pattern","query","description"]){const d=t[a];if(typeof d=="string"&&d.trim()){const c=d.trim().split(`
|
|
2
|
+
`)[0]??"";return c.length>48?`${c.slice(0,45)}...`:c}}}function Z(e){switch(e){case"none":return"existing claude login or cloud provider";case"/login managed key":return"key managed by claude login";default:return e}}function O(e){switch(e){case"error_max_turns":return"the agent hit its turn limit before finishing";case"error_max_budget_usd":return"the agent hit its cost limit before finishing";case"error_during_execution":return"the agent stopped on an error";default:return`the agent stopped: ${e}`}}const q=new Set(["Edit","Write","NotebookEdit"]);function ee(e,t){return["This directory is shared with other conversations that may be running at the same time.",`Before you edit, create, or delete any file here, call EnterWorktree with name "${t}"`,"so your changes land in a git worktree of your own. Load the tool with ToolSearch first if","it isn't available. If a worktree by that name already exists, enter it by path instead:",`${U(e,t)}.`,"","Once you are in the worktree, work normally. Relative paths resolve inside it, and you","stay in it for every later message in this thread. Commit what you finish on the","worktree's branch and say which branch it is, since that's how a person finds your work.","Don't call ExitWorktree."].join(`
|
|
3
|
+
`)}function te(e){const t=k(e);return({toolName:r,cwd:o})=>{if(q.has(r)&&k(o)===t)return"This is the shared checkout, which other conversations are using. Call EnterWorktree with the name given in your instructions, then make this edit inside the worktree."}}function ne(e,t,r,o){const a=x(e)?ee(o,r):void 0;return[Q(t),a].filter(d=>!!d?.trim()).join(`
|
|
4
4
|
|
|
5
|
-
`)}function
|
|
6
|
-
${
|
|
5
|
+
`)}function re(e,t){return{model:t?.model??e.model,effort:t?.effort??e.effort}}function oe(e){return async t=>{const r=t,o=e({toolName:r.tool_name,cwd:r.cwd});return o?(h.info({tool:r.tool_name,cwd:r.cwd},"refusing an edit in the shared checkout"),{continue:!0,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:o}}):{continue:!0}}}function ie(e,t=L()){const r=e.CLAUDE_CONFIG_DIR;return r?{allow:[r]}:{allow:[S(t,".claude")],allowFiles:[S(t,".claude.json")].filter(K)}}async function se(e){const t=await $(e);if(!t||k(t)===k(e))return[];const r=S(t,G);return await F(r,{recursive:!0}),[r]}const ae=2048;function v(e,t){const r=t.text.trim();return r?`${e}
|
|
6
|
+
${r}`:e}function de(e,t,r){return o=>{const a=ie(o.env);for(const f of a.allow??[])j(f,{recursive:!0});const d=N(o.command),c=P(e,{allow:[...a.allow??[],...t],read:d?[d]:[],...a.allowFiles?{allowFiles:a.allowFiles}:{}},o.command,o.args),m=D(c.command,c.args,{...o.cwd?{cwd:o.cwd}:{},env:o.env,stdio:["pipe","pipe","pipe"],signal:o.signal});return m.stderr.on("data",f=>{r.text=(r.text+f.toString()).slice(-ae)}),m.on("exit",f=>{const g=r.text.trim();f&&g&&h.warn({code:f,stderr:g},"the sandboxed agent exited with an error")}),m}}function _e(e={}){const t=A(e.options,B);t.length>0&&h.warn({ignored:t},"these agent.options are set by Bishop and cannot be overridden; ignoring them");const r=n=>re(e,n),o=r(),a=z(e.options,o);a.length>0&&h.warn({shadowed:a},"agent.model and agent.effort override these agent.options");const d=A(e.options,H);d.length>0&&h.warn({misplaced:d},"the harness reads these from agent.options.settings only and ignores them here");const c=e.worktrees,m=e.projectSettings!==!1;if(typeof e.options?.settings=="string"){const n=["auto memory is left to that file and the agent's own settings"];x(c)&&n.push("new worktrees branch from origin/<default-branch> rather than HEAD"),m&&n.push("the agent's .claude/settings.json gets only what headless Claude trusts of it, without its allow rules or plugins"),h.warn({settings:e.options.settings},`agent.options.settings names a file, so Bishop applies no settings defaults: ${n.join("; ")}`)}const f=(n,s,u,p,l,i)=>{const w=x(c)?te(n.cwd):void 0,y=w?{PreToolUse:[{hooks:[oe(w)]}]}:void 0,b=W({...e.options?{configOptions:e.options}:{},general:r(u),...e.apiKey?{apiKey:e.apiKey}:{},...y?{hooks:y,worktrees:!0}:{},...s?{tools:s}:{},...l?{agentSettings:l}:{},...i?{installPlugins:i}:{},controlled:n});return p?{...b,spawnClaudeCodeProcess:p}:b},g=m&&typeof e.options?.settings!="string",E=(n,s)=>g?M(n,s):Promise.resolve(void 0),T=async(n,s,u)=>de(s,x(c)?await se(n):[],u);return{name:"claude",describeStartupFailure(n,s){return`The Claude agent couldn't start in ${n}:
|
|
7
7
|
${s}
|
|
8
8
|
|
|
9
9
|
Bishop does not manage Anthropic credentials. The agent authenticates the
|
|
10
10
|
same way \`claude\` does in this directory, so make sure that works first:
|
|
11
11
|
ANTHROPIC_API_KEY in the environment or .env, or
|
|
12
|
-
an existing \`claude\` login on this machine.`},async check(
|
|
12
|
+
an existing \`claude\` login on this machine.`},async check(n,s){const u=new AbortController,p={text:""};try{const l=s?await T(n,s,p):void 0;await E(n,s);for await(const i of I({prompt:"ping",options:{...f({cwd:n,systemPrompt:void 0,abortController:u},void 0,void 0,l),maxTurns:1,persistSession:!1}})){if(i.type==="system"&&i.subtype==="init")return u.abort(),{ok:!0,credentialSource:Z(i.apiKeySource),...i.model?{model:i.model}:{}};if(i.type==="result")break}return{ok:!1,error:v("the harness exited without starting a session",p)}}catch(l){if(u.signal.aborted)return{ok:!0};const i=l instanceof Error?l.message:String(l);return{ok:!1,error:v(i,p)}}},async*runTurn(n){const s=new AbortController,u=()=>s.abort();n.signal.addEventListener("abort",u,{once:!0});let p=!1;const l={text:""};try{const i=n.sandbox?await T(n.cwd,n.sandbox,l):void 0,w=await E(n.cwd,n.sandbox),y=m&&!n.sessionId,b=y?Y({thread:n.threadKey}):void 0,C=I({prompt:J(n),options:f({cwd:n.cwd,systemPrompt:{type:"preset",preset:"claude_code",append:ne(c,n.origin,n.worktree,n.cwd)},abortController:s,...n.sessionId?{resume:n.sessionId}:{}},n.tools,n,i,w,y)});for await(const R of C){b?.(R);for(const _ of V(R))(_.type==="done"||_.type==="error")&&(p=!0),yield _}}catch(i){s.signal.aborted?h.debug({thread:n.threadKey},"claude turn aborted"):p?h.debug({err:i},"query() threw after reporting a result"):(h.error({err:i},"harness turn failed"),yield{type:"error",kind:"harness_failure",message:v(i instanceof Error?i.message:String(i),l)})}finally{n.signal.removeEventListener("abort",u)}}}}export{re as chosenGeneral,_e as claudeHarness,ie as claudeStateGrants,te as editGuard,ne as systemPromptAppend,X as toolDetail,ee as worktreeNote};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{spawn as B}from"node:child_process";import{randomBytes as I}from"node:crypto";import{mkdir as N,realpath as P,rename as E,rm as H,stat as A}from"node:fs/promises";import{basename as R,dirname as M,isAbsolute as T,join as k,relative as W,resolve as G,sep as $}from"node:path";import{createInterface as U}from"node:readline";import{exec as X}from"../exec.js";import{log as O}from"../log.js";import{SERVER_NAME as L}from"../tools/server.js";import{isPlainObject as c,PLUGIN_INSTALL_TIMEOUT_MS as Y,unionLists as z}from"./layers.js";const C=1e4,_=".codex";function S(e){return typeof e=="string"?JSON.stringify(e):typeof e=="number"?Number.isNaN(e)?"nan":Number.isFinite(e)?String(e):e>0?"inf":"-inf":typeof e=="boolean"?String(e):Array.isArray(e)?`[${e.filter(t=>t!=null).map(S).join(",")}]`:c(e)?`{${Object.entries(e).filter(([,n])=>n!=null).map(([n,s])=>`${JSON.stringify(n)}=${S(s)}`).join(",")}}`:JSON.stringify(String(e))}function D(e,t){const n=c(e)?{...e}:{};for(const[s,r]of Object.entries(t)){const o=Object.hasOwn(n,s)?n[s]:void 0;n[s]=c(o)&&c(r)?{...o,...r}:r}return n}function pe(e,t){const n=Object.fromEntries(e.map(s=>[s,{trust_level:"trusted"}]));return`projects=${S(D(t,n))}`}function me(e,t){if(e.length===0)return;const n=Object.fromEntries(e.map(o=>[o.key,{trusted_hash:o.currentHash}])),s=c(t)?t:{},r={...s,state:D(s.state,n)};return`hooks=${S(r)}`}function F(e){const t=e?.plugins;return c(t)?Object.entries(t).filter(([,n])=>c(n)&&n.enabled===!0).map(([n])=>n):[]}function ge(e,t,n){const s=n.map(r=>k(r,_)+$);return e.filter(r=>r.source==="project"&&s.some(o=>r.sourcePath?.startsWith(o)??!1)||r.source==="plugin"&&r.pluginId!==null&&t.includes(r.pluginId))}function K(e,t){return Array.isArray(e)&&Array.isArray(t)?z(t,e):c(e)&&c(t)?Object.fromEntries(Object.entries(e).map(([n,s])=>[n,K(s,Object.hasOwn(t,n)?t[n]:void 0)])):e}const he=["mcp_servers","notify","hooks","projects"];function j(e,t){let n=e;for(const s of t){if(!c(n)||!Object.hasOwn(n,s))return;n=n[s]}return n}const J=[{option:"sandboxMode",key:["sandbox_mode"]},{option:"networkAccessEnabled",key:["sandbox_workspace_write","network_access"]}];function we(e,t){const n=c(t?.config)?t.config:void 0,s={...e};for(const{option:r,key:o}of J)!(t!==void 0&&Object.hasOwn(t,r))&&j(n,o)!==void 0&&delete s[r];return s}function ye(e,t,n){const s=c(t?.config)?t.config:void 0,r={...e},o=[];for(const{option:f,key:g}of J)!(t!==void 0&&Object.hasOwn(t,f)||j(s,g)!==void 0)&&j(n,g)!==void 0&&Object.hasOwn(r,f)&&(delete r[f],o.push(f));return{options:r,yielded:o}}function be(e){const t=e.developer_instructions;return typeof t=="string"&&t.trim()?t:void 0}async function ke(e){return(await A(k(e,_)).catch(()=>{}))?.isDirectory()??!1}async function q(e){const t=await P(e).catch(()=>e);return[...new Set([e,t])]}async function V(e,t,n,s){const r=B(e.command,[...t,"app-server"],{cwd:e.cwd,env:e.env,stdio:["pipe","pipe","pipe"],...s?{signal:s,killSignal:"SIGKILL"}:{}});let o="";r.stderr.setEncoding("utf8"),r.stderr.on("data",p=>{o=(o+p).slice(-2e3)}),r.stdin.on("error",()=>{});const f=U({input:r.stdout}),g=[];let l=0;const y=p=>r.stdin.write(`${JSON.stringify(p)}
|
|
2
|
+
`);try{return await new Promise((p,w)=>{const u=setTimeout(()=>w(new Error(`codex app-server didn't answer within ${C}ms`)),C),d=a=>{clearTimeout(u),a?w(a):p(g)},h=()=>{const a=n[l];if(!a)return d();l+=1,y({id:l,...a})};f.on("line",a=>{let m;try{m=JSON.parse(a)}catch{return}m.id===l&&(g.push(m),l===1&&y({method:"initialized"}),h())}),r.on("error",a=>d(a)),r.on("exit",a=>d(new Error(`codex app-server exited with code ${a}: ${o.trim()}`))),h()})}finally{f.close(),r.stdin.end(),r.kill()}}async function Oe(e,t,n){const s=e.cwd;let r;try{r=await V(e,t.flatMap(i=>["-c",i]),[{method:"initialize",params:{clientInfo:{name:"bishop",version:"0"}}},{method:"config/read",params:{includeLayers:!0,cwd:s}},{method:"hooks/list",params:{cwds:[s]}}],n)}catch(i){return{ok:!1,reason:i instanceof Error?i.message:String(i)}}const[,o,f]=r;if(o?.error)return{ok:!1,reason:o.error.message??"config/read failed"};const g=o?.result?.layers,l=(await q(s)).map(i=>k(i,_)),p=(Array.isArray(g)?g:[]).find(i=>c(i)&&c(i.name)&&i.name.type==="project"&&l.includes(String(i.name.dotCodexFolder))&&!i.disabledReason&&c(i.config))?.config??{};if(c(p.mcp_servers)&&Object.hasOwn(p.mcp_servers,L))return{ok:!1,reason:`it declares an MCP server named "${L}", which is Bishop's`};f?.error&&O.warn({cwd:s,error:f.error.message},"codex couldn't list the agent's hooks, so none are approved this turn");const w=f?.result?.data?.[0],u=c(w)?w:{},d=[u.warnings,u.errors].flatMap(i=>Array.isArray(i)?i:[]).map(i=>c(i)?String(i.message):String(i));d.length>0&&O.warn({cwd:s,warnings:d},"codex reported problems with the agent's hooks; it skips those");const h=(Array.isArray(u.hooks)?u.hooks:[]).filter(i=>c(i)&&typeof i.key=="string"&&typeof i.currentHash=="string"),a=o?.result?.config,m=c(a)?a.plugins:void 0,b=c(m)?Object.entries(m).filter(([,i])=>c(i)&&i.enabled===!1).map(([i])=>i):[];return{ok:!0,project:{config:p,hooks:h,disabledPlugins:b}}}function Q(e,t){if(typeof e.source!="string"||!e.source)return;const n=e.source_type==="local"&&!T(e.source)?G(t,e.source):e.source,s=typeof e.ref=="string"&&e.ref?[`--ref=${e.ref}`]:[],r=Array.isArray(e.sparse_paths)?e.sparse_paths.filter(o=>typeof o=="string").map(o=>`--sparse=${o}`):[];return["plugin","marketplace","add",...s,...r,"--json","--",n]}function Z(e){const t=e.lastIndexOf("@");if(!(t<=0||t===e.length-1))return{plugin:e.slice(0,t),marketplace:e.slice(t+1)}}async function xe(e){const{launch:t,home:n,agent:s,operator:r,installed:o,context:f}=e,g=c(s.marketplaces)?s.marketplaces:{},l=new Map;for(const u of F(s)){if(j(r,["plugins",u,"enabled"])===!1||e.disabled?.includes(u))continue;const d=Z(u);if(d){if(!Object.hasOwn(g,d.marketplace)){await A(k(n,"plugins","cache",d.marketplace,d.plugin)).then(()=>!0).catch(()=>!1)||O.warn({...f,plugin:u},"the agent enables a plugin from a marketplace its config doesn't declare, so Bishop can't install it");continue}l.set(d.marketplace,[...l.get(d.marketplace)??[],u])}}const y=AbortSignal.timeout(e.timeoutMs??Y),p=[...l].flatMap(([u,d])=>{const h=g[u],a=c(h)?Q(h,t.cwd):void 0;if(!a)return O.warn({...f,marketplace:u},"the agent's marketplace declaration has no source, so its plugins aren't installed"),[];const m=JSON.stringify([n,a,d.sort()]),b=o.get(m);if(b?.done)return[];if(b)return[b.pending];const i={done:!1,pending:te({...e,name:u,addArgs:a,plugins:d,signal:y}).then(x=>(x.ok?i.done=!0:o.delete(m),x.added))};return o.set(m,i),[i.pending]});return p.length===0?!1:(await ee(Promise.all(p),e.signal))?.some(Boolean)??!1}function ee(e,t){return t?t.aborted?Promise.resolve(void 0):new Promise((n,s)=>{const r=()=>n(void 0);t.addEventListener("abort",r,{once:!0}),e.then(o=>{t.removeEventListener("abort",r),n(o)},o=>{t.removeEventListener("abort",r),s(o)})}):e}async function te(e){const{launch:t,home:n,name:s,addArgs:r,plugins:o,signal:f,context:g}=e,l={...g,marketplace:s};let y=!1;const p=k(n,".tmp",`bishop-plugins-${I(6).toString("hex")}`);try{await N(p,{recursive:!0});const w=await P(p),u=a=>X(t.command,a,{cwd:t.cwd,env:{...t.env,CODEX_HOME:w},signal:f}),d=await u(r);if(d.code!==0)return O.warn({...l,error:d.output.trim()||"timed out"},"plugins from a marketplace failed to install"),{ok:!1,added:y};let h=!0;for(const a of o){const m=await u(["plugin","add","--json","--",a]),b=ne(m.stdout);if(m.code!==0||!b){O.warn({...l,plugin:a,error:m.output.trim()||"timed out"},"a plugin failed to install"),h=!1;continue}const i=M(b),x=W(w,i);if(x.startsWith("..")||T(x)||!x.startsWith(k("plugins","cache")+$)){O.warn({...l,plugin:a,path:b},"codex installed a plugin somewhere Bishop did not expect"),h=!1;continue}const v=k(n,x);await A(k(v,R(b))).catch(()=>{})||(await re(i,v,w),y=!0)}return h&&O.info({...l,plugins:o},"installed plugins from a marketplace"),{ok:h,added:y}}catch(w){return O.warn({...l,err:w},"plugins from a marketplace failed to install"),{ok:!1,added:y}}finally{await H(p,{recursive:!0,force:!0}).catch(()=>{})}}function ne(e){try{const t=JSON.parse(e);return typeof t.installedPath=="string"?t.installedPath:void 0}catch{return}}async function re(e,t,n){await N(M(t),{recursive:!0});const s=k(n,`replaced-${I(4).toString("hex")}`),r=await E(t,s).then(()=>!0,o=>{if(o.code!=="ENOENT")throw o;return!1});try{await E(e,t)}catch(o){const f=o.code;if(f==="ENOTEMPTY"||f==="EEXIST")return;throw r&&await E(s,t).catch(()=>{}),o}}export{_ as CODEX_DIR,he as WHOLE_KEYS,ge as agentHooks,be as agentInstructions,K as combineLists,F as enabledPlugins,ke as hasProjectConfig,me as hookApprovals,Oe as inspectProject,xe as installPlugins,S as tomlInline,pe as trustOverride,q as trustedDirs,ye as yieldToAgent,we as yieldToOperatorConfig};
|
package/dist/harness/codex.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{existsSync as
|
|
2
|
-
`)}function
|
|
1
|
+
import{existsSync as ae}from"node:fs";import{mkdir as X}from"node:fs/promises";import{homedir as Y}from"node:os";import{delimiter as ce,dirname as U,join as P,resolve as $,sep as de}from"node:path";import{Codex as W}from"@openai/codex-sdk";import{BishopError as F}from"../errors.js";import{log as f}from"../log.js";import{executableDir as le,launchEnv as ue,nonoCommand as fe}from"../sandbox.js";import{SERVER_NAME as he}from"../tools/server.js";import{ensureWorktree as pe,gitDirs as ge,isolates as me}from"../worktrees.js";import{agentHooks as we,agentInstructions as ye,combineLists as xe,enabledPlugins as be,hasProjectConfig as _e,hookApprovals as ve,inspectProject as Ee,installPlugins as ke,trustedDirs as Oe,trustOverride as G,WHOLE_KEYS as V,yieldToAgent as Ce,yieldToOperatorConfig as Se}from"./codex-project.js";import{ignoredKeys as Te,layerOptions as De,mergeEnv as Pe,shadowedKeys as Ae}from"./layers.js";import{buildPrompt as je,principalInstructions as Ke}from"./principal.js";const Me="agent.sandbox is on, but this Codex harness was built without Bishop's sandbox launcher",A="danger-full-access";function z(e){if(e){const r=e.includes(de)?$(e):e,d=le(r);return{command:r,pathDirs:[],read:d?[d]:[]}}const n=new W().exec,s=n?.executablePath,a=n?.pathDirs;if(typeof s!="string"||!Array.isArray(a))throw new F("agent.sandbox is on, but Bishop couldn't find the Codex binary the SDK runs. Set agent.options.codexPathOverride to its path.");return{command:s,pathDirs:a,read:[U(U(s))]}}function J(e,n){return e.pathDirs.length===0?{}:{PATH:[...e.pathDirs,n].filter(Boolean).join(ce)}}function j(e,n=Y()){return e.CODEX_HOME??P(n,".codex")}const Ie=10,Re=60;function Le(e,n){const s=e??void 0;return n?{...s,[he]:{url:n.url,bearer_token_env_var:n.tokenEnvVar,required:!0,default_tools_approval_mode:"auto",startup_timeout_sec:Ie,tool_timeout_sec:Re}}:s}const Be={approvalPolicy:"never",sandboxMode:"workspace-write",networkAccessEnabled:!0,skipGitRepoCheck:!0},Q=["workingDirectory","developer_instructions"];function He(e){const n=e?.config,s=n&&typeof n=="object"&&"developer_instructions"in n?["config.developer_instructions"]:[];return[...Te(e,Q),...s]}const Ne=new Set(["apiKey","baseUrl","config","configOverrides","env","codexPathOverride"]);function Xe(e){switch(e.type){case"command_execution":return{type:"tool",name:"Bash"};case"file_change":{const n=e.changes.length;if(n>1)return{type:"tool",name:"Edit",detail:`${n} files`};const s=n===1?e.changes[0]?.path.split("/").pop():void 0;return{type:"tool",name:"Edit",...s?{detail:s}:{}}}case"mcp_tool_call":return{type:"tool",name:`mcp__${e.server}__${e.tool}`};case"web_search":return{type:"tool",name:"WebSearch",...e.query?{detail:e.query}:{}};case"todo_list":return{type:"tool",name:"TodoWrite"};case"reasoning":return{type:"thinking"};default:return}}const Ye=new Set(["command_execution","mcp_tool_call","web_search","reasoning"]);function Ue(e){const n=new Set;let s="";return a=>{switch(a.type){case"thread.started":return[{type:"session",sessionId:a.thread_id,...e?{model:e}:{}}];case"item.started":case"item.completed":{const r=a.item,d=Ye.has(r.type)?"item.started":"item.completed";if(a.type!==d||n.has(r.id))return[];if(n.add(r.id),r.type==="agent_message")return s=r.text,r.text.trim()?[{type:"text",text:r.text}]:[];if(r.type==="error")return f.warn({message:r.message},"codex reported a non-fatal error"),[];const b=Xe(r);return b?[b]:[]}case"turn.completed":return[{type:"done",text:s,usage:{inputTokens:a.usage.input_tokens,cachedInputTokens:a.usage.cached_input_tokens,outputTokens:a.usage.output_tokens}}];case"turn.failed":return[{type:"error",kind:"turn_failed",message:a.error.message}];case"error":return[{type:"error",kind:"stream_error",message:a.message}];default:return[]}}}function $e(e){return[`You are working in a git worktree of your own, on the branch "${e}", created for this`,"conversation. Other conversations are working in their own worktrees at the same time.","","Work normally. Commit what you finish on this branch and say which branch it is, since that's","how a person finds your work."].join(`
|
|
2
|
+
`)}function We(e,n){const s=Array.isArray(e)?e:[],a=[...new Set([...s,...n??[]])];return a.length>0?{additionalDirectories:a}:{}}function Z(e){return{model:e.model,modelReasoningEffort:e.effort}}function Fe(e){return Object.fromEntries(Object.entries(Z(e)).filter(([,n])=>n!==void 0))}const Ge={model:"model",modelReasoningEffort:"model_reasoning_effort"};function Ve(e,n){const s=e?.config,a=s&&typeof s=="object"?Object.entries(Ge).filter(([r,d])=>n[r]!==void 0&&d in s).map(([,r])=>`config.${r}`):[];return[...Ae(e,n),...a]}function ze(e,n){if(!n)return e;const s=Object.fromEntries(V.filter(r=>Object.hasOwn(e,r)).map(r=>[r,e[r]])),a=Object.fromEntries(Object.entries(e).filter(([r])=>!V.includes(r)));return{...xe(a,n.config),...s}}function Je(e,n){const{projects:s,hooks:a,...r}=e,d=ve(we(n.hooks,be(n.config),n.dirs),a);return{overrides:[G(n.dirs,s),...d?[d]:[]],config:d||a===void 0?r:{...r,hooks:a}}}function ft(e={}){const n=He(e.options);n.length>0&&f.warn({ignored:n},"these agent.options are set by Bishop and cannot be overridden; ignoring them");const s=Z(e),a=Ve(e.options,s);a.length>0&&f.warn({shadowed:a},"agent.model and agent.effort override these agent.options");const r=De({defaults:Be,...e.options?{configOptions:e.options}:{},general:s,controlledKeys:Q}),d={},b={};for(const[t,i]of Object.entries(r))Ne.has(t)?d[t]=i:b[t]=i;const y=Se(b,e.options),C=d.env;e.apiKey&&(d.apiKey=e.apiKey);const q=e.worktrees,x=e.launcher,E=x?z(d.codexPathOverride):void 0;x&&f.info({sandboxMode:A,replacing:y.sandboxMode},"agent.sandbox is on, so Codex's own sandbox is off and nono is the only one");const K=async(t,i,c=[])=>{if(!E)throw new F(Me);const l={...process.env,...C},o=j(l);await X(o,{recursive:!0});const h=fe(t,{allow:[o,...c],read:E.read},E.command,[]);return{...ue(h,i),...J(E,l.PATH)}},M=e.projectSettings!==!1,ee=new Map,I=new Set,R=(d.configOverrides??[]).filter(t=>/^\s*(projects|hooks)\s*[=.]/.test(t));M&&R.length>0&&f.warn({configOverrides:R},"these agent.options.configOverrides are replaced by Bishop's own on turns that apply the agent's .codex config; set them in agent.options.config instead");let k;const te=()=>{if(k===void 0)try{k=z(d.codexPathOverride)}catch(t){f.warn({err:t},"couldn't find the Codex binary, so turns run without the agent's .codex config"),k=null}return k??void 0},ne=async(t,i)=>{const c=Object.fromEntries(Object.entries({...process.env,...C}).filter(m=>m[1]!==void 0)),l=$(j(c)),o={...c,CODEX_HOME:l};if(i&&x)return{command:x,cwd:t,env:{...o,...i}};const h=te();if(h){try{await X(l,{recursive:!0})}catch(m){f.warn({err:m,home:l},"couldn't create CODEX_HOME, so this turn runs without the agent's .codex config");return}return{command:h.command,cwd:t,env:{...o,...J(h,o.PATH)}}}},S=async(t,i)=>{const c=await Oe(t.cwd),l=[...d.configOverrides??[],G(c)],o=await Ee(t,l,i);if(o.ok)return{...o.project,dirs:c};i?.aborted||f.warn({cwd:t.cwd,reason:o.reason},"Codex can't load the agent's .codex config, so this turn runs without it")},L=async(t,i)=>M&&await _e(t)?ne(t,i):void 0,B=(t,i,c,l,o)=>{const h=ze(d.config??{},o),m=o?Je(h,o):void 0,w=m?.config??h,p=Le(w.mcp_servers,c),g=Pe(C,{...c?{[c.tokenEnvVar]:c.token}:{},...l}),u={...w,developer_instructions:[i&&Ke(i),t,o&&ye(o.config)].filter(v=>!!v?.trim()).join(`
|
|
3
3
|
|
|
4
|
-
`)};
|
|
4
|
+
`)};p&&(u.mcp_servers=p);const _=[...d.configOverrides??[],...m?.overrides??[]];return new W({...d,...l&&x?{codexPathOverride:x}:{},...g?{env:g}:{},..._.length>0?{configOverrides:_}:{},config:u})};async function oe(t){if(!me(q))return{cwd:t.cwd};const i=await pe(t.cwd,t.worktree),c=await ge(i);return c.length===0&&f.warn({worktree:i},"couldn't resolve the worktree's git directories, so the agent probably can't commit"),{cwd:i,note:$e(t.worktree),...c.length>0?{writable:c}:{}}}return{name:"codex",describeStartupFailure(t,i){return`The Codex agent couldn't start in ${t}:
|
|
5
5
|
${i}
|
|
6
6
|
|
|
7
7
|
Bishop does not manage OpenAI credentials. The agent authenticates the
|
|
8
8
|
same way \`codex\` does on this machine, so make sure that works first:
|
|
9
9
|
CODEX_API_KEY in the environment or .env, or
|
|
10
|
-
an existing \`codex login\` on this machine.`},async check(
|
|
10
|
+
an existing \`codex login\` on this machine.`},async check(t,i){const c=new AbortController,l=y.model;try{const o=i?await K(i,t):void 0,h=await L(t,o);h&&await S(h);const w=B(void 0,void 0,void 0,o).startThread({...y,...o?{sandboxMode:A}:{},workingDirectory:t}),{events:p}=await w.runStreamed("ping",{signal:c.signal});for await(const g of p){if(g.type==="thread.started"){c.abort();const u=Qe(e.apiKey);return Ze(u),{ok:!0,...u?{credentialSource:u}:{},...l?{model:l}:{}}}if(g.type==="error")return{ok:!1,error:g.message}}return{ok:!1,error:"the harness exited without starting a thread"}}catch(o){return c.signal.aborted?{ok:!0}:{ok:!1,error:o instanceof Error?o.message:String(o)}}},async*runTurn(t){const i=new AbortController,c=()=>i.abort();t.signal.addEventListener("abort",c,{once:!0});let l=!1;try{const{cwd:o,note:h,writable:m}=await oe(t),w=t.sandbox?await K(t.sandbox,o,m):void 0,p=await L(o,w),g=i.signal;let u=p?await S(p,g):void 0;if(p&&u&&!t.sessionId&&await ke({launch:p,home:j(p.env),agent:u.config,operator:d.config,disabled:u.disabledPlugins,installed:ee,context:{thread:t.threadKey},signal:g})&&(u=await S(p,g)??u),g.aborted)return;const _=u?Ce(y,e.options,u.config):void 0,v=(_?.yielded??[]).filter(O=>!(w&&O==="sandboxMode")),H=v.join(",");v.length>0&&!I.has(H)&&(I.add(H),f.info({cwd:o,yielded:v},"the agent's .codex config sets these itself, so Bishop's defaults for them step aside"));const N=B(h,t.origin,t.tools,w,u),T={..._?.options??y,...Fe(t),...w?{sandboxMode:A}:{},workingDirectory:o,...We(y.additionalDirectories,m)},re=t.sessionId?N.resumeThread(t.sessionId,T):N.startThread(T),ie=Ue(T.model),{events:se}=await re.runStreamed(je(t),{signal:i.signal});for await(const O of se)for(const D of ie(O))(D.type==="done"||D.type==="error")&&(l=!0),yield D}catch(o){i.signal.aborted?f.debug({thread:t.threadKey},"codex turn aborted"):l?f.debug({err:o},"codex threw after reporting a result"):(f.error({err:o},"harness turn failed"),yield{type:"error",kind:"harness_failure",message:o instanceof Error?o.message:String(o)})}finally{t.signal.removeEventListener("abort",c)}}}}function Qe(e){if(e)return"CODEX_API_KEY";const n=process.env.CODEX_HOME??P(Y(),".codex");if(ae(P(n,"auth.json")))return"existing codex login"}function Ze(e){e||f.warn("no CODEX_API_KEY and no codex login found, so the first turn will probably fail to authenticate; run `codex login`, or set CODEX_API_KEY")}export{Q as CONTROLLED_CODEX_KEYS,Be as DEFAULT_CODEX_OPTIONS,Fe as chosenCodexOptions,z as codexBinary,Z as codexGeneralOptions,ft as codexHarness,j as codexHome,Ue as codexTranslator,He as ignoredCodexKeys,Le as mergeMcpServers,Je as projectOverrides,Ve as shadowedCodexKeys,ze as withAgentLists,We as withWritable,$e as worktreeNote};
|
package/dist/harness/create.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{claudeHarness as
|
|
1
|
+
import{claudeHarness as s}from"./claude.js";import{codexHarness as c}from"./codex.js";function d(o,e){const r={...e.options?{options:e.options}:{},...e.model?{model:e.model}:{},...e.effort?{effort:e.effort}:{},...e.worktrees?{worktrees:e.worktrees}:{},...e.projectSettings===!1?{projectSettings:!1}:{}};switch(o){case"codex":return c({...r,...e.launcher?{launcher:e.launcher}:{},...e.credentials.codexApiKey?{apiKey:e.credentials.codexApiKey}:{}});case"claude":return s({...r,...e.credentials.anthropicApiKey?{apiKey:e.credentials.anthropicApiKey}:{}})}}export{d as createHarness};
|
package/dist/harness/layers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function o(e){const
|
|
1
|
+
function o(e){const n={...e.defaults,...e.configOptions};for(const[r,t]of Object.entries(e.general??{}))t!==void 0&&(n[r]=t);for(const r of e.controlledKeys)delete n[r];return n}function i(e,n){return!e||!n?[]:Object.entries(n).filter(([r,t])=>t!==void 0&&r in e).map(([r])=>r)}function s(e,n){return e?n.filter(r=>r in e):[]}function f(e,n){if(!(!e&&Object.keys(n).length===0))return{...process.env,...e,...n}}const u=6e4;function c(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function d(e,n){const r=new Set(e.map(t=>JSON.stringify(t)));return[...e,...n.filter(t=>!r.has(JSON.stringify(t)))]}export{u as PLUGIN_INSTALL_TIMEOUT_MS,s as ignoredKeys,c as isPlainObject,o as layerOptions,f as mergeEnv,i as shadowedKeys,d as unionLists};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentdeploymentco/bishop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "An agent gateway. Connects an agent to Slack, email, and Microsoft Teams.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"docs:preview": "npm --prefix docs run preview --"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
48
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.296",
|
|
49
49
|
"@microsoft/teams.apps": "^2.0.16",
|
|
50
50
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
51
|
-
"@openai/codex-sdk": "0.
|
|
51
|
+
"@openai/codex-sdk": "0.162.1",
|
|
52
52
|
"@slack/bolt": "^5.0.0",
|
|
53
53
|
"@slack/web-api": "^8",
|
|
54
54
|
"commander": "^13",
|