@agentdeploymentco/bishop 0.8.1 → 0.8.2
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/harness/claude-processes.js +1 -1
- package/dist/harness/claude.js +8 -7
- package/dist/interface/phrases.js +1 -1
- package/dist/slack/agent-session.js +1 -1
- package/dist/slack/handlers.js +1 -1
- package/dist/slack/interface.js +2 -2
- package/dist/slack/presence.js +1 -1
- package/dist/teams/interface.js +3 -3
- package/dist/teams/presence.js +1 -1
- package/dist/turn.js +2 -2
- package/package.json +5 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as
|
|
1
|
+
import{randomUUID as L}from"node:crypto";import{log as a}from"../log.js";const F={idleMs:10*6e4,max:4},E=5e3;class C{items=[];waiting;done=!1;failure;push(o){if(!this.done)if(this.waiting){const h=this.waiting;this.waiting=void 0,h({value:o,done:!1})}else this.items.push(o)}end(o){if(!this.done&&(this.done=!0,this.failure=o,this.waiting)){const h=this.waiting;this.waiting=void 0,h({value:void 0,done:!0})}}async*[Symbol.asyncIterator](){for(;;){const o=this.items.shift();if(o!==void 0){yield o;continue}if(this.done){if(this.failure)throw this.failure;return}const h=await new Promise(b=>{this.waiting=b});if(h.done){if(this.failure)throw this.failure;return}yield h.value}}}const P=new Set(["local_agent","local_workflow"]),q="main-session";function j(d){return d.type==="system"&&d.subtype==="init"||K(d)||d.type==="result"}function K(d){return d.type==="assistant"&&!d.parent_tool_use_id}function I(d){return!!(d.turn||d.current||d.background.size>0)}function R(d,o){const h=JSON.parse(d),b=JSON.parse(o);return[...new Set([...Object.keys(h),...Object.keys(b)])].filter(w=>JSON.stringify(h[w])!==JSON.stringify(b[w]))}function V(d){const o=new Map;let h=0;const b=new WeakMap;let w;const{keepAlive:v}=d;function c(e,t){if(e.closing)return;e.closing=t,e.idleTimer&&clearTimeout(e.idleTimer),o.get(e.threadId)===e&&o.delete(e.threadId),e.keptAlive&&w?.ended(e.threadId,e.id);const n={thread:e.threadKey,reason:t,turns:e.turns,aliveMs:Date.now()-e.startedAt,...e.background.size>0?{background:e.background.size}:{}};e.keptAlive?a.info(n,"closing a thread's Claude Code process"):a.debug(n,"closing a thread's Claude Code process"),e.inbox.end(),e.query.close()}function x(e){if(e.idleTimer&&clearTimeout(e.idleTimer),e.idleTimer=void 0,!(!v||!e.keptAlive||e.closing)){if(I(e)){!e.turn&&!e.current&&a.debug({thread:e.threadKey,background:e.background.size},"keeping Claude Code running while its background work runs");return}e.idleTimer=setTimeout(()=>c(e,"idle"),v.idleMs),e.idleTimer.unref()}}function S(e){if(!v)return;const t=[...o.values()].filter(s=>s.keptAlive&&!s.closing);let n=t.length-v.max;if(n<=0)return;const r=t.filter(s=>s!==e&&!I(s)).sort((s,i)=>s.lastUsed-i.lastUsed);for(const s of r){if(n<=0)break;c(s,"over the cap"),n--}n>0&&a.debug({kept:t.length,max:v.max},"over the keep-alive cap, but the rest are busy")}function D(e){const t=e.turn?.onOwned;!t||!e.turn||(e.turn.onOwned=void 0,t())}function A(e,t,n){if(t.owner=n,n==="turn"&&e.turn)t.channel=e.turn.channel,D(e);else{const r=new C;t.channel=r;const s=t.buffer[0];a.info({thread:e.threadKey,...s?{opened:"subtype"in s?`${s.type}/${s.subtype}`:s.type}:{}},"the agent started a follow-up"),w?w.followUp(e.threadId,d.translate(r),()=>t.delegating??e.delegated.size>0):(a.warn({thread:e.threadKey},"nothing is listening for a follow-up; dropping it"),(async()=>{for await(const i of r);})())}for(const r of t.buffer.splice(0))t.channel.push(r)}function O(e,t){if(!e.current){if(!j(t)||e.turns===0&&!e.turn){e.turn?e.turn.channel.push(t):e.turns===0&&e.early.push(t);return}e.idleTimer&&clearTimeout(e.idleTimer),e.idleTimer=void 0,e.current={buffer:[]}}const n=e.current;if(t.type==="system"&&t.subtype==="init"&&(n.init??=t,b.set(t,Date.now())),n.owner)e.turn&&n.channel!==e.turn.channel&&K(t)&&t.user_message_uuid===e.turn.uuid?(n.channel?.end(),n.owner==="followUp"&&(e.notified=!1),n.owner="turn",n.channel=e.turn.channel,D(e),n.init&&n.channel.push(n.init),n.channel.push(t)):n.channel?.push(t);else if(n.buffer.push(t),K(t)||t.type==="result"){const r=K(t),s=r?t.user_message_uuid:void 0,i=!!e.turn&&(s?s===e.turn?.uuid:!r||!e.notified);A(e,n,i?"turn":"followUp")}t.type==="result"&&(n.delegating=e.delegated.size>0,n.channel?.end(),e.current=void 0,e.lastUsed=Date.now(),n.owner==="turn"?e.turn?.channel===n.channel&&(e.turn=void 0):(e.notified=!1,a.info({thread:e.threadKey,outcome:t.subtype},"a follow-up finished")),x(e))}async function U(e){let t;try{for await(const s of e.query)if(s.type==="system"&&s.subtype==="init"&&(e.sessionId=s.session_id),s.type==="system"&&s.subtype==="task_notification"&&!s.ambient&&(e.notified=!0),O(e,s),s.type==="system"&&s.subtype==="background_tasks_changed"){const i=e.background.size,f=s.tasks.filter(u=>!u.ambient);e.background=new Set(f.map(u=>u.task_id)),e.delegated=new Set(f.filter(u=>P.has(u.task_type)&&u.subagent_type!==q).map(u=>u.task_id)),e.background.size!==i&&a.debug({thread:e.threadKey,background:e.background.size},"background work changed"),x(e)}}catch(s){t=s}e.closing||(a.warn({thread:e.threadKey,turns:e.turns,aliveMs:Date.now()-e.startedAt,...t?{err:t}:{}},"a thread's Claude Code process exited on its own"),c(e,"exited"));const n=t instanceof Error?t.message:"Claude Code exited before the turn finished",r=new Error(e.explain?e.explain(n):n);e.current?.channel?.end(r),e.turn?.channel.end(r),e.current=void 0,e.turn=void 0}async function z(e,t,n){const r=Date.now(),s=new C,i=`cc_${++h}`,f=t?w?.started(e.threadId,i):e.tools;let u,_;try{u=await e.options(),_=d.start({prompt:s,options:u.options})}catch(p){throw t&&w?.ended(e.threadId,i),p}const{explain:m}=u,g={id:i,threadId:e.threadId,threadKey:e.threadKey,query:_,inbox:s,fingerprint:JSON.stringify(e.fingerprint),keptAlive:t,...e.sessionId?{sessionId:e.sessionId}:{},startedAt:r,lastUsed:r,turns:0,notified:!1,background:new Set,delegated:new Set,exited:Promise.resolve(),...m?{explain:m}:{},early:[]};if(g.exited=U(g),t&&o.set(e.threadId,g),f){const p=Date.now();try{const y=await _.setMcpServers(e.server(f)),k=Object.entries(y.errors??{});k.length>0&&(a.warn({thread:e.threadKey,errors:Object.fromEntries(k)},"the agent couldn't connect to Bishop's tools; this turn runs without them"),g.toolless=!0)}catch(y){a.warn({thread:e.threadKey,err:y},"couldn't hand the agent Bishop's tools; this turn runs without them"),g.toolless=!0}a.debug({thread:e.threadKey,startMs:Date.now()-r,toolsMs:Date.now()-p},"Claude Code is up")}const T={thread:e.threadKey,reason:n,startMs:Date.now()-r};return t?a.info(T,"started Claude Code for this thread"):a.debug(T,"started Claude Code for one turn"),g}async function N(e,t){const n=o.get(e.threadId);if(!n)return"new";if(n.closing)return"exited";const r=JSON.stringify(e.fingerprint);let s;if(!t)s="one-off turn";else if(n.fingerprint!==r){const i=R(n.fingerprint,r);if(I(n))return a.info({thread:e.threadKey,changed:i},"the agent's configuration changed, but it is still running something; keeping it until it isn't"),n;a.info({thread:e.threadKey,changed:i},"the agent's configuration changed since its process started; starting a new one"),s="configuration changed"}else n.sessionId!==e.sessionId&&(s="session changed");return s?(c(n,s),await n.exited,s):n}async function*B(e){const t=Date.now(),n=!!v&&e.keepAlive,r=await N(e,n),s=typeof r!="string";s&&a.debug({thread:e.threadKey,idleMs:t-r.lastUsed,...r.current?{behind:"a follow-up"}:{}},"reusing the thread's Claude Code process");const i=s?r:await z(e,n,r);if(i.closing){await i.exited;const l="Claude Code exited before the turn started";throw new Error(i.explain?i.explain(l):l)}if(e.signal.aborted){n?i.toolless?c(i,"Bishop's tools didn't attach"):(x(i),S(i)):c(i,"one-off turn");return}i.idleTimer&&clearTimeout(i.idleTimer),i.idleTimer=void 0;const f=new C,u=L();i.turn={uuid:u,channel:f},i.turns++;for(const l of i.early.splice(0))f.push(l);const _=Date.now()-t;i.inbox.push({type:"user",message:{role:"user",content:e.text},parent_tool_use_id:null,session_id:i.sessionId??"",uuid:u});let m=!1,g;const T=()=>{a.debug({thread:e.threadKey},"interrupting the agent"),i.query.interrupt().catch(l=>{a.debug({thread:e.threadKey,err:l},"the interrupt was not delivered")}),g=setTimeout(()=>{a.warn({thread:e.threadKey},"the agent didn't stop when asked; closing its process"),c(i,"didn't stop when asked")},E),g.unref()},p=()=>{m=!0;const l=i.turn?.channel===f,J=i.current&&i.current.channel!==f;if(l&&J&&i.turn){a.debug({thread:e.threadKey},i.current?.owner==="followUp"?"holding the stop until the follow-up ends":"holding the stop until the running turn's first reply says whose it is"),i.turn.onOwned=T;return}T()};e.signal.aborted?p():e.signal.addEventListener("abort",p,{once:!0});let y,k,M=!1;try{for await(const l of f)l.type==="result"&&(M=!0),!m&&(y===void 0&&l.type==="system"&&l.subtype==="init"&&(y=(b.get(l)??Date.now())-t),k===void 0&&l.type==="assistant"&&(k=Date.now()-t),yield l)}finally{e.signal.removeEventListener("abort",p),g&&clearTimeout(g),a.debug({thread:e.threadKey,process:s?"kept":"new",readyMs:_,...y===void 0?{}:{initMs:y},...k===void 0?{}:{firstOutputMs:k},totalMs:Date.now()-t,...m?{stopped:!0}:{}},"process timing"),!M&&i.turn?.channel===f&&!i.closing&&(m||(a.debug({thread:e.threadKey},"the turn ended before the agent did; interrupting it"),i.query.interrupt().catch(()=>{})),setTimeout(()=>{i.turn?.channel===f&&c(i,"didn't stop when asked")},E).unref()),n?i.toolless?c(i,"Bishop's tools didn't attach"):(x(i),S(i)):c(i,"one-off turn")}}return{run:B,attach(e){w=e},has(e){return o.has(e)},async close(){const e=[...o.values()];for(const t of e)c(t,"shutting down");await Promise.allSettled(e.map(t=>t.exited))}}}export{F as DEFAULT_KEEP_ALIVE,V as claudeProcesses};
|
package/dist/harness/claude.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import{spawn as
|
|
2
|
-
`)[0]??"";return
|
|
3
|
-
`)}function
|
|
1
|
+
import{spawn as N}from"node:child_process";import{existsSync as P,mkdirSync as $}from"node:fs";import{mkdir as U}from"node:fs/promises";import{homedir as B}from"node:os";import{join as E,resolve as x}from"node:path";import{query as D}from"@anthropic-ai/claude-agent-sdk";import{log as h}from"../log.js";import{executableDir as F,nonoCommand as G}from"../sandbox.js";import{SERVER_NAME as T}from"../tools/server.js";import{isolates as _,repoRoot as W,WORKTREE_DIR as M,worktreePath as H}from"../worktrees.js";import{bishopServer as O,buildAgentOptions as Y,CONTROLLED_KEYS as V,SETTINGS_ONLY_KEYS as z}from"./claude-options.js";import{claudeProcesses as J,DEFAULT_KEEP_ALIVE as Q}from"./claude-processes.js";import{pluginInstallWatcher as X,readAgentSettings as Z}from"./claude-settings.js";import{ignoredKeys as j,shadowedKeys as q}from"./layers.js";import{buildPrompt as ee,NO_OUTPUT as te,principalInstructions as ne}from"./principal.js";const oe=new Set(["Agent","Task"]),re=2e3;function ie(e){if(!e||typeof e!="object")return;const{name:n,description:o}=e;for(const r of[n,o]){if(typeof r!="string"||!r.trim())continue;const i=r.trim().split(`
|
|
2
|
+
`)[0]??"";return i.length>40?`${i.slice(0,37)}...`:i}}function se(){const e=new Map,n=new Map,o=(r,i,c)=>{if(r.set(i,c),r.size>re){const s=r.keys().next().value;s!==void 0&&r.delete(s)}};return function*(i){switch(i.type){case"system":i.subtype==="init"&&(yield{type:"session",sessionId:i.session_id,...i.model?{model:i.model}:{}});return;case"assistant":{const c=i.parent_tool_use_id||null,s=c===null?void 0:e.get(c)??"Subagent";for(const a of i.message.content)if(a.type==="text"&&a.text.trim())s||(yield{type:"text",text:a.text});else if(a.type==="tool_use"){const u=oe.has(a.name)?ie(a.input):void 0;u&&o(e,a.id,u),s&&o(n,a.id,s);const g=u??ae(a.input);yield{type:"tool",name:a.name,...g?{detail:g}:{},...s?{agent:s}:{}}}else a.type==="thinking"&&!s&&(yield{type:"thinking"});return}case"tool_use_summary":{const c=i.preceding_tool_use_ids.map(s=>n.get(s)).find(s=>s!==void 0);yield{type:"tool_summary",summary:i.summary,...c?{agent:c}:{}};return}case"result":i.subtype==="success"?yield{type:"done",text:i.result,usage:{turns:i.num_turns,costUsd:i.total_cost_usd}}:yield{type:"error",kind:i.subtype,message:de(i.subtype)};return;default:return}}}function ae(e){if(!e||typeof e!="object")return;const n=e,o=n.file_path??n.notebook_path??n.path;if(typeof o=="string"&&o)return o.split("/").pop()??o;const r=n.url;if(typeof r=="string"&&r)try{return new URL(r).hostname}catch{return r.slice(0,40)}for(const i of["pattern","query","description"]){const c=n[i];if(typeof c=="string"&&c.trim()){const s=c.trim().split(`
|
|
3
|
+
`)[0]??"";return s.length>48?`${s.slice(0,45)}...`:s}}}function ce(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 de(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 le=new Set(["Edit","Write","NotebookEdit"]);function ue(e,n){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 "${n}"`,"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:",`${H(e,n)}.`,"","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(`
|
|
4
|
+
`)}function fe(e){const n=x(e);return({toolName:o,cwd:r})=>{if(le.has(o)&&x(r)===n)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."}}const pe=["When background work you started finishes after your reply, you are woken to report on it, and what you say is posted to the conversation.","Report on each subagent as it finishes, the way you would to someone waiting on the work.","Some conversations show only the report you make once all your subagents have finished, so make that one stand on its own: everything the people there need from all of the work, not only the piece that just finished.",`When nothing from the work needs saying, answer ${te} and nothing else.`].join(" ");function he(e,n,o,r,i=!1){const c=_(e)?ue(r,o):void 0;return[ne(n),i?pe:void 0,c].filter(s=>!!s?.trim()).join(`
|
|
4
5
|
|
|
5
|
-
`)}function
|
|
6
|
-
${o}`:e}function
|
|
7
|
-
${
|
|
6
|
+
`)}function me(e,n){return{model:n?.model??e.model,effort:n?.effort??e.effort}}function ge(e){return async n=>{const o=n,r=e({toolName:o.tool_name,cwd:o.cwd});return r?(h.info({tool:o.tool_name,cwd:o.cwd},"refusing an edit in the shared checkout"),{continue:!0,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:r}}):{continue:!0}}}function ye(e,n=B()){const o=e.CLAUDE_CONFIG_DIR;return o?{allow:[o]}:{allow:[E(n,".claude")],allowFiles:[E(n,".claude.json")].filter(P)}}async function we(e){const n=await W(e);if(!n||x(n)===x(e))return[];const o=E(n,M);return await U(o,{recursive:!0}),[o]}const ke=2048;function A(e,n){const o=n.text.trim();return o?`${e}
|
|
7
|
+
${o}`:e}function be(e){return n=>A(n,e)}function xe(e,n,o){return r=>{const i=ye(r.env);for(const u of i.allow??[])$(u,{recursive:!0});const c=F(r.command),s=G(e,{allow:[...i.allow??[],...n],read:c?[c]:[],...i.allowFiles?{allowFiles:i.allowFiles}:{}},r.command,r.args),a=N(s.command,s.args,{...r.cwd?{cwd:r.cwd}:{},env:r.env,stdio:["pipe","pipe","pipe"],signal:r.signal});return a.stderr.on("data",u=>{o.text=(o.text+u.toString()).slice(-ke)}),a.on("exit",u=>{const g=o.text.trim();u&&g&&h.warn({code:u,stderr:g},"the sandboxed agent exited with an error")}),a}}function Ue(e={}){const n=j(e.options,V);n.length>0&&h.warn({ignored:n},"these agent.options are set by Bishop and cannot be overridden; ignoring them");const o=e.options?.mcpServers;o&&typeof o=="object"&&Object.hasOwn(o,T)&&h.warn({server:T},`agent.options.mcpServers names a server "${T}", which is Bishop's own; ignoring it`);const r=t=>me(e,t),i=r(),c=q(e.options,i);c.length>0&&h.warn({shadowed:c},"agent.model and agent.effort override these agent.options");const s=j(e.options,z);s.length>0&&h.warn({misplaced:s},"the harness reads these from agent.options.settings only and ignores them here");const a=e.worktrees,u=e.projectSettings!==!1;if(typeof e.options?.settings=="string"){const t=["auto memory is left to that file and the agent's own settings"];_(a)&&t.push("new worktrees branch from origin/<default-branch> rather than HEAD"),u&&t.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: ${t.join("; ")}`)}const g=(t,l,d,m,p)=>{const f=_(a)?fe(t.cwd):void 0,b=f?{PreToolUse:[{hooks:[ge(f)]}]}:void 0,y=Y({...e.options?{configOptions:e.options}:{},general:r(l),...e.apiKey?{apiKey:e.apiKey}:{},...b?{hooks:b,worktrees:!0}:{},...m?{agentSettings:m}:{},...p?{installPlugins:p}:{},controlled:t});return d?{...y,spawnClaudeCodeProcess:d}:y},C=u&&typeof e.options?.settings!="string",R=(t,l)=>C?Z(t,l):Promise.resolve(void 0),v=e.keepAlive??Q,I=se(),S=J({start:e.start??(t=>D(t)),keepAlive:v,async*translate(t){for await(const l of t)yield*I(l)}}),K=async(t,l,d)=>xe(l,_(a)?await we(t):[],d);return{name:"claude",describeStartupFailure(t,l){return`The Claude agent couldn't start in ${t}:
|
|
8
|
+
${l}
|
|
8
9
|
|
|
9
10
|
Bishop does not manage Anthropic credentials. The agent authenticates the
|
|
10
11
|
same way \`claude\` does in this directory, so make sure that works first:
|
|
11
12
|
ANTHROPIC_API_KEY in the environment or .env, or
|
|
12
|
-
an existing \`claude\` login on this machine.`},async check(t,
|
|
13
|
+
an existing \`claude\` login on this machine.`},async check(t,l){const d=new AbortController,m={text:""};try{const p=l?await K(t,l,m):void 0;await R(t,l);for await(const f of D({prompt:"ping",options:{...g({cwd:t,systemPrompt:void 0,abortController:d},void 0,p),maxTurns:1,persistSession:!1}})){if(f.type==="system"&&f.subtype==="init")return d.abort(),{ok:!0,credentialSource:ce(f.apiKeySource),...f.model?{model:f.model}:{}};if(f.type==="result")break}return{ok:!1,error:A("the harness exited without starting a session",m)}}catch(p){if(d.signal.aborted)return{ok:!0};const f=p instanceof Error?p.message:String(p);return{ok:!1,error:A(f,m)}}},async*runTurn(t){let l=!1;try{const d=await R(t.cwd,t.sandbox),m=u&&!t.sessionId,p=m?X({thread:t.threadKey}):void 0,f=!!v&&t.keepAlive===!0,b=he(a,t.origin,t.worktree,t.cwd,f),y=r(t),L=S.run({threadId:t.threadId,threadKey:t.threadKey,text:ee(t),fingerprint:{cwd:t.cwd,origin:{name:t.origin.name,self:t.origin.self},model:y.model,effort:y.effort,settings:d,sandbox:t.sandbox},...t.sessionId?{sessionId:t.sessionId}:{},keepAlive:f,...t.tools?{tools:t.tools}:{},server:O,signal:t.signal,options:async()=>{const w={text:""},k=t.sandbox?await K(t.cwd,t.sandbox,w):void 0;return{options:g({cwd:t.cwd,systemPrompt:{type:"preset",preset:"claude_code",append:b},abortController:new AbortController,...t.sessionId?{resume:t.sessionId}:{}},t,k,d,m),explain:be(w)}}});for await(const w of L){p?.(w);for(const k of I(w))(k.type==="done"||k.type==="error")&&(l=!0),yield k}}catch(d){t.signal.aborted?h.debug({thread:t.threadKey},"claude turn aborted"):l?h.debug({err:d},"the agent failed after reporting a result"):(h.error({err:d},"harness turn failed"),yield{type:"error",kind:"harness_failure",message:d instanceof Error?d.message:String(d)})}},...v?{keepAlive:{attach:t=>S.attach(t),close:()=>S.close()}}:{}}}export{pe as BACKGROUND_REPORTS,me as chosenGeneral,Ue as claudeHarness,ye as claudeStateGrants,se as claudeTranslator,fe as editGuard,he as systemPromptAppend,ae as toolDetail,ue as worktreeNote};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{FETCH_FILE_TOOL as
|
|
1
|
+
import{FETCH_FILE_TOOL as s}from"../harness/principal.js";import{SHARE_FILE_TOOL as c}from"../outgoing.js";import{READ_MESSAGE_TOOL as u}from"./messages.js";import{FIND_PERSON_TOOL as l}from"./people.js";const g={Read:e=>e?`Reading ${e}`:"Reading a file",Write:e=>e?`Writing ${e}`:"Writing a file",Edit:e=>e?`Editing ${e}`:"Editing a file",NotebookEdit:e=>e?`Editing ${e}`:"Editing a notebook",Bash:e=>e??"Running a command",BashOutput:()=>"Checking on a command",Glob:e=>e?`Looking for ${e}`:"Looking through files",Grep:e=>e?`Searching for ${e}`:"Searching the code",WebFetch:e=>e?`Reading ${e}`:"Fetching a page",WebSearch:e=>e?`Searching for ${e}`:"Searching the web",Agent:e=>e?`Handing off ${e}`:"Handing off a subtask",Task:e=>e?`Handing off ${e}`:"Handing off a subtask",TodoWrite:()=>"Planning",ExitPlanMode:()=>"Finishing up a plan",AskUserQuestion:()=>"Considering options"},f={schedule_create:"Setting up a schedule",schedule_list:"Checking what is scheduled",schedule_update:"Changing a schedule",schedule_delete:"Removing a schedule",[s]:"Fetching a file from this conversation",[u]:"Reading a message from this conversation",[l]:"Looking somebody up in this conversation",[c]:"Getting a file ready to send"};function h(e){if(!e.startsWith("mcp__"))return;const[,n,...t]=e.split("__");return n==="bishop"?f[t.join("__")]??"Sorting something out":n?`Calling ${n}`:"Calling a tool"}function d(e){const n=e.replaceAll(/[_-]+/g," ").replaceAll(/([a-z\d])([A-Z])/g,"$1 $2").trim();if(!n)return"Working";const t=n.toLowerCase();return t.charAt(0).toUpperCase()+t.slice(1)}const r=100;function i(e){const n=e.trim();return n.length>r?`${n.slice(0,r-3)}...`:n}function a(e,n){return n?`${n}: ${e}`:e}function k(e){return a(i(e.summary),e.agent)}function R(e){return a(m(e),e.agent)}function m(e){const n=e.summary?.trim();if(n)return i(n);const t=g[e.name];if(t)return t(e.detail);const o=h(e.name);return o||d(e.name)}function p(e){const n=Math.max(0,Math.round(e/1e3)),t=Math.floor(n/60),o=n%60;return t?`${t}m ${o}s`:`${o}s`}function A(e){return e===1?"Used 1 tool":`Used ${e} tools`}function L(e,n){return`${n?"Worked for":"Stopped after"} ${p(e)}`}export{p as elapsed,d as humanizeToolName,k as retitlePhrase,i as summaryPhrase,R as toolPhrase,A as toolTally,L as turnTally};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{log as i}from"../log.js";import{plainText as h}from"./mentions.js";async function
|
|
1
|
+
import{log as i}from"../log.js";import{plainText as h}from"./mentions.js";async function p(o,n,t,e){try{await o.agents.sessions.setStatus({channel_id:n.channelId,thread_ts:n.threadTs,status:t,...e?{initiator_user_id:e}:{}})}catch(r){i.debug({...n,status:t,err:r},"could not set the agent session status")}}async function f(o,n,t){const e=m(t);if(e)try{await o.agents.sessions.rename({channel_id:n.channelId,thread_ts:n.threadTs,title:e})}catch(r){const s=[...e].length;i.warn({...n,length:s,err:r},"could not name the agent session")}}const l=80,d=/[^\p{L}\p{M}\p{Nd} !"&'(),\-.=?_|]/gu;function m(o){const t=h(o).normalize("NFC").replace(d," ").replace(/\s+/g," ").trim();if(!t)return"";if([...t].length<=l)return t;let e="",r=0;for(const{segment:a}of new Intl.Segmenter().segment(t)){const c=[...a].length;if(r+c>l-3)break;e+=a,r+=c}const s=e.trimEnd();return s?`${s}...`:""}export{f as renameSession,m as sessionTitle,p as setSessionStatus};
|
package/dist/slack/handlers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{log as r}from"../log.js";import{markHeld as R,releaseHeld as F}from"./pending.js";import{FILE_SHARE as L,isBotEvent as H,isSelfEvent as O,routeAppMention as W,routeMessage as Y}from"./route.js";import{say as $}from"./say.js";import{mentionsBot as C,messageKeyString as G,spokenAt as J,threadKeyOf as K,threadKeyString as A}from"./thread.js";const Q=new Set(["channel_join","channel_leave","channel_name","group_join","group_leave","group_name"]);function ne(I,j){const{identity:d,botName:m,threads:x,events:q,run:z,stop:D,allows:_,bots:p,participants:T,command:B,changed:N}=j,l=new Map,y=new Map;async function E(e,s
|
|
1
|
+
import{log as r}from"../log.js";import{markHeld as R,releaseHeld as F}from"./pending.js";import{FILE_SHARE as L,isBotEvent as H,isSelfEvent as O,routeAppMention as W,routeMessage as Y}from"./route.js";import{say as $}from"./say.js";import{mentionsBot as C,messageKeyString as G,spokenAt as J,threadKeyOf as K,threadKeyString as A}from"./thread.js";const Q=new Set(["channel_join","channel_leave","channel_name","group_join","group_leave","group_name"]);function ne(I,j){const{identity:d,botName:m,threads:x,events:q,run:z,stop:D,allows:_,bots:p,participants:T,command:B,changed:N}=j,l=new Map,y=new Map;async function E(e,a,s){const n=l.get(a);if(!n?.length)return[];l.set(a,[]),await y.get(a);const t=await F(s.client,e,n,{botUserId:d.botUserId,...m?{botName:m}:{}}),c=[];for(const h of t)_(h.userId)?c.push(h):r.warn({thread:a,user:h.userId},"dropping a held message");return c}async function P(e,a,s){e.subtype&&Q.has(e.subtype)&&e.channel&&N?.(e.channel);const n=K(e,d.teamId);if(!n){r.debug({source:s},"dropping event with no usable thread key");return}const t=A(n),c=await x.has(t),h={botUserId:d.botUserId,botId:d.botId,...m?{botName:m}:{},threadKnown:c},u=s==="app_mention"?W(e,h):Y(e,h);if((c||u.action==="run")&&e.user&&!O(e,h)&&(!e.subtype||e.subtype===L)&&T.saw(t,e.user,J(e.ts)),u.action==="ignore"){e.user&&e.user!==d.botUserId&&!H(e)&&p.clear(t),r.debug({source:s,thread:t,reason:u.reason},"ignoring event");return}const f=H(e);if(f&&!p.answersBots){r.debug({source:s,thread:t},"ignoring a bot: this agent answers none");return}const w=G(n,e);if(w&&await q.seen(`slack:${w}`)){r.info({source:s,thread:t,message:w,eventId:e.event_id},"duplicate message, skipping");return}const o=e.user??"";if(!_(o)){if(f){r.debug({source:s,thread:t,user:o},"ignoring a bot off the allow list");return}r.warn({source:s,thread:t,user:o},"refusing an unauthorized request");try{await a.client.chat.postEphemeral({channel:n.channelId,user:o,thread_ts:n.threadTs,text:"You're not on this agent's allow list."})}catch(i){r.debug({err:i},"could not post the refusal")}return}const S=await x.remember(t);if(B&&await B({key:n,keyString:t,thread:S,text:u.text},a)){f||p.clear(t),r.info({source:s,thread:t,user:o},"ran a command");return}const b=p.admit(t,f);if(!b.run){const i={source:s,thread:t,user:o},g="holding off: too many bot turns in a row in this thread";if(b.say?r.warn(i,g):r.debug(i,g),b.say)try{await $(a.client,{channel:n.channelId,threadTs:n.threadTs},b.say)}catch(k){r.debug({err:k},"could not say why Bishop is holding off")}return}const v={text:u.text,userId:o,ts:e.ts??n.threadTs,isBot:f,mentionsAgent:C(e.text??"",d.botUserId),files:u.files},U=l.get(t);if(U){U.push(v),r.info({source:s,thread:t,user:o},"holding a message until the turn ends");const i=(y.get(t)??Promise.resolve()).then(()=>R(a.client,n.channelId,v.ts));y.set(t,i),await i;return}l.set(t,[]);try{let i=[v],g=!c;for(;i.length||l.get(t)?.length;){if(!i.length){i=await E(n,t,a);continue}r.info({thread:t,messages:i.length,newThread:g},"running a turn");try{await z({key:n,keyString:t,thread:S,channelId:n.channelId,threadTs:n.threadTs,messages:i,isNewThread:g,participants:T.count(t),isDirectMessage:e.channel_type==="im"||n.channelId.startsWith("D")},a)}catch(k){r.error({thread:t,err:k},"turn failed")}g=!1,i=await E(n,t,a)}}finally{l.delete(t),y.delete(t)}}async function M(e,a,s){try{await P(e,a,s)}catch(n){r.error({source:s,err:n},"failed to handle a Slack event")}}I.event("app_mention",async({event:e,body:a,client:s})=>{await M({...e,event_id:a.event_id},{client:s},"app_mention")}),I.event("message",async({event:e,body:a,client:s})=>{await M({...e,event_id:a.event_id},{client:s},"message")}),I.event("agent_session_stopped",async({event:e})=>{const a=K(e,d.teamId);if(!a)return;if(!_(e.user)){r.warn({user:e.user},"ignoring a stop from an unauthorized user");return}const s=A(a),n=D(s);r.info({thread:s,user:e.user,aborted:n},"stop requested")})}export{ne as registerHandlers};
|
package/dist/slack/interface.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{runCommand as O}from"../commands/run.js";import{commandLine as U}from"../commands/syntax.js";import{etiquetteNote as C,threadParticipants as L}from"../interface/etiquette.js";import{threadVerbosity as
|
|
2
|
-
`)},
|
|
1
|
+
import{runCommand as O}from"../commands/run.js";import{commandLine as U}from"../commands/syntax.js";import{etiquetteNote as C,threadParticipants as L}from"../interface/etiquette.js";import{showsProgress as E,threadVerbosity as H}from"../interface/verbosity.js";import{log as f}from"../log.js";import{createSlackApp as F}from"./app.js";import{createAuthorizer as K}from"./authorizer.js";import{botTurnCap as _}from"./bots.js";import{slackObserver as R}from"./context.js";import{deliverToSlack as z,describeSlackHandle as W,parseSlackHandle as v,resolveSlackDestination as j,slackHandle as q}from"./destination.js";import{describeSlackFile as G,parseSlackFile as V,slackAttachment as S,slackFilePayload as Y}from"./files.js";import{registerHandlers as $}from"./handlers.js";import{landedMidThread as J,threadHistory as Q}from"./history.js";import{linkMentions as T,mentionableName as X,renderMentions as M}from"./mentions.js";import{createPresence as Z}from"./presence.js";import{slackSighting as ee}from"./principal.js";import{slackMessageReader as te}from"./read.js";import{say as x}from"./say.js";import{threadKeyString as ae}from"./thread.js";import{userDirectory as ne}from"./users.js";const oe={name:"slack",guidance:["This conversation is a Slack thread. Each thing you write is posted as its own message while","you work, and the tools you call show as a card between them, so someone is watching the","whole turn. Say what you are doing as you go, and keep it to a line or two. Your last message","is the answer to what was asked. Slack renders standard markdown, tables included, so write","normally and keep it short.","People and other agents are named to you as @name. Write @name to mention one and Bishop","makes it a real Slack mention; a name it cannot match to one account stays plain text, so","use the name you were given. @here, @channel, and @everyone are never mentions, whoever","writes them.","A thread can hold a conversation between other people that has nothing to do with you.",'Bishop marks a message you may leave alone with a <thread> tag carrying reply="optional":',"other people are talking here and this one did not name you, so answer it only if you have","something worth adding. To stay out of it, say <no-output> and nothing else, and Bishop","posts nothing at all. Every message without that tag is yours to answer."].join(`
|
|
2
|
+
`)},re=3e3;async function se(a,i,o=re){const l=await ie(a.botUserId,i,o),r=X(l)??a.username;if(!r)return;const h=[a.appName,l.realName].filter(w=>!!w?.trim()&&w?.toLowerCase()!==r.toLowerCase());return{address:`@${r}`,...h.length>0?{aka:[...new Set(h)]}:{}}}async function ie(a,i,o){const l={userId:a};let r;const h=new Promise(w=>{r=setTimeout(()=>w(l),o)});try{return await Promise.race([i.lookup(a).catch(()=>l),h])}finally{clearTimeout(r)}}async function Pe(a){const{app:i,identity:o}=await F(a.credentials),l=await K(i.client,a.allow),r=ne(i.client,{self:o.botUserId}),h=_(a.maxBotTurns),w=L(),I=R({client:i.client,users:r,home:o,self:o.botUserId}),k=H(a.meta,"slack",a.verbosity),N={verbosity:k,model:a.model,configured:a.configuredModel??{},stop:a.stop},c=await se(o,r),u={...oe,...c?{self:c}:{}};c?f.info({address:c.address,aka:c.aka},"the agent's name in Slack"):f.warn("could not read the bot's own name; the agent will not know what it is called");const b=new Map,P=async({key:e,keyString:t,thread:s,text:m},d)=>{const n=U(m);if(!n)return!1;const p=O(n,{thread:s},N);try{await x(d.client,{channel:e.channelId,threadTs:e.threadTs},p),b.get(t)?.interrupted()}catch(g){f.warn({thread:t,err:g},"couldn't answer a command")}return!0};async function y(e,t,s){const m=await r.lookup(e),d=ee(m,t,o);return a.principals.see(u.name,s?{...d,self:s}:d)}y(o.botUserId,!0,!0).catch(e=>{f.warn({err:e},"could not record the bot's own account")});function B(e,t){const s=G(t);if(!s.unavailable)return a.attachments.remember(e,u.name,{id:s.id,name:s.name,payload:Y(t)})}async function D(e,t){return t.files.length===0?[]:Promise.all(t.files.map(async s=>{const m=B(e.id,s),d=await a.files.save(e.dirKey,S(s,a.credentials.botToken));return m?{...d,ref:m}:d}))}function A(e,t){const s=M(e.messages[0]?.text??"",r),m=J(e),d=C({participants:e.participants,mentioned:e.messages.some(n=>n.mentionsAgent)});return{thread:e.thread,isNewThread:e.isNewThread,origin:u,...d?{note:d}:{},...m?{history:()=>Q({client:t.client,key:e.key,before:m,allows:n=>l.allows(n),answersBots:h.answersBots,self:{botUserId:o.botUserId,...o.botId?{botId:o.botId}:{},...c?{botName:c.address}:{}},users:r,principal:y,remember:n=>a.attachments.remember(e.thread.id,u.name,n),saw:(n,p)=>w.saw(e.keyString,n,p)})}:{},observe:()=>I.observe({channelId:e.channelId,direct:e.isDirectMessage,speaker:e.messages.at(-1)?.userId??""}),async messages(){return Promise.all(e.messages.map(async(n,p)=>{const g=await D(e.thread,n);return{principal:await y(n.userId,n.isBot),externalId:n.ts,text:p===0?await s:await M(n.text,r),...g.length>0?{files:g}:{}}}))},showsProgress:()=>E(k.of(e.thread.id).level),presence:()=>{const n=Z(e,t,{outgoing:p=>T(p,r),title:()=>s,verbosity:()=>k.of(e.thread.id).level,...d?{mayDecline:!0}:{},...a.taskIntervalMs===void 0?{}:{taskIntervalMs:a.taskIntervalMs}});return b.set(e.keyString,n),{...n,finish:async()=>{try{await n.finish()}finally{b.delete(e.keyString)}}}},async notify(n){await x(t.client,{channel:e.channelId,threadTs:e.threadTs},n)},async destination(){const n=q({channel:e.channelId,threadTs:e.threadTs});return{interface:"slack",handle:n,label:await W(i.client,n)}}}}return $(i,{identity:o,...c?{botName:c.address}:{},threads:a.threads,events:a.events,run:(e,t)=>a.run(A(e,t)),stop:a.stop,allows:e=>l.allows(e),bots:h,participants:w,command:P,changed:e=>I.changed(e)}),{name:"slack",async start(){await i.start(),o.scopes.includes("files:read")||f.warn("this Slack app has no files:read scope, so it cannot open files people share; reinstall it to grant one"),o.scopes.includes("files:write")||f.warn("this Slack app has no files:write scope, so it cannot send files back; reinstall it to grant one"),r.prime(),f.info("listening on Slack via Socket Mode")},async stop(){l.stop(),await i.stop()},allows:e=>l.allows(e.externalId),resolveDestination:(e,t)=>j(i.client,e,{...t?.optedInOnly?{allows:s=>l.allows(s)}:{}}),sameConversation:(e,t)=>v(e).channel===v(t).channel,deliver:async e=>{const t=await z(i.client,{...e,text:await T(e.text,r)});return t?{threadKey:ae({teamId:o.teamId,channelId:t.channel,threadTs:t.threadTs})}:{}},readMessage:te({client:i.client,users:r,principal:y,self:{botUserId:o.botUserId,...o.botId?{botId:o.botId}:{},...c?{botName:c.address}:{}},remember:(e,t)=>a.attachments.remember(e,u.name,t)}),attachment:e=>{const t=V(e);return t?S(t,a.credentials.botToken):void 0}}}export{oe as SLACK_ORIGIN,Pe as createSlackInterface,se as slackIdentity};
|
package/dist/slack/presence.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{DEFAULT_VERBOSITY as H}from"../config.js";import{saysNoOutput as b}from"../harness/principal.js";import{
|
|
1
|
+
import{DEFAULT_VERBOSITY as H}from"../config.js";import{saysNoOutput as b}from"../harness/principal.js";import{retitlePhrase as N,toolPhrase as U,turnTally as V}from"../interface/phrases.js";import{showsProgress as Y,showsTasks as B}from"../interface/verbosity.js";import{log as y}from"../log.js";import{fileShelf as q}from"../outgoing.js";import{renameSession as z,setSessionStatus as O}from"./agent-session.js";import{splitForSlack as G}from"./format.js";import{say as j}from"./say.js";import{taskMessage as J}from"./tasks.js";import{attachToSlack as K}from"./upload.js";function Q(r){const l=[];for(const a of r){if(typeof a=="string"){l.push({text:a.trim(),files:[]});continue}const n=l.at(-1);n?n.files.push(a):l.push({text:"",files:[a]})}return l}const W=3e4;function lt(r,l,a={}){const n=l.client,u={channel:r.channelId,threadTs:r.threadTs},L=Date.now(),R=()=>a.verbosity?.()??H;let c,x,$="",i=!1,p="",A=!1,d,D=!1;const w=q();async function g(t,e){try{return await e()}catch(o){y.debug({thread:r.keyString,what:t,err:o},"presence call failed");return}}let S=C();function k(t){return S=S.then(()=>O(n,r,t)),S}async function C(){const t=r.messages[0];if(await O(n,r,"processing",t?.userId),!(!a.title||t?.ts!==r.threadTs))try{await z(n,r,await a.title())}catch(e){y.debug({thread:r.keyString,err:e},"could not read the session title")}}async function T(){const t=c;t&&(c=void 0,await g("task.close",()=>t.close()))}async function _(t){const e=await g("say",()=>j(n,u,t));return e?(x=e,i=!0,!0):(i=!1,!1)}async function E(t,e){const o=t?G(t):[],s=o.pop()??"";let f=!0;for(const h of o)await _(h)||(f=!1);const m=e.map(h=>h.name).join(", ");let I,F=!1;try{I=await K(n,u,e,s)}catch(h){F=!0,y.error({thread:r.keyString,files:m,err:h},"could not upload a file")}return I?(x=I,i=!!s,f):(s&&!await _(s)&&(f=!1),F&&await g("say",()=>j(n,u,`_Could not upload ${m}._`)),i=!1,f)}async function M(t){await T();const e=w.place(t);let o=!0;for(const s of Q(e.parts))(s.files.length?await E(s.text,s.files):await _(s.text))||(o=!1);o&&($=e.text)}const P=setInterval(()=>{k("processing")},a.heartbeatMs??W);return P.unref(),{interrupted(){i=!1},share:t=>w.offer(t),async handle(t){const e=R();switch(B(e)||await T(),t.type){case"text":{if(b(t.text)||!t.text.trim()||!Y(e))return;await M(a.outgoing?await a.outgoing(t.text):t.text);return}case"tool":if(!B(e))return;c||(c=J(n,u,a.taskIntervalMs),i=!1),c.note(U(t));return;case"tool_summary":if(!B(e))return;c?.retitle(N(t));return;case"done":A=!0,p=b(t.text)?"":t.text,D=!!a.mayDecline&&b(t.text);return;case"error":d=t.message;return;default:return}},async finish(){if(clearInterval(P),await T(),D&&!d){y.info({thread:r.keyString},"the agent left this message alone"),await k("active");return}const t=V(Date.now()-L,A||!!d),e=p.trim()&&a.outgoing?await a.outgoing(p):p,o=d?`Something went wrong: ${d}`:w.strip(e)===$.trim()?"":e.trim(),s=x;!o&&s&&i&&await g("coda",()=>n.chat.update({channel:u.channel,ts:s.ts,markdown_text:`${s.text}
|
|
2
2
|
|
|
3
3
|
_${t}_`}))||await M([o,`_${t}_`].filter(Boolean).join(`
|
|
4
4
|
|
package/dist/teams/interface.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{runCommand as
|
|
2
|
-
`)}function Kn(o){return
|
|
1
|
+
import{runCommand as on}from"../commands/run.js";import{BishopError as an}from"../errors.js";import{etiquetteNote as rn,threadParticipants as sn}from"../interface/etiquette.js";import{splitMessage as cn}from"../interface/format.js";import{showsProgress as G,showsTasks as ln,threadVerbosity as hn}from"../interface/verbosity.js";import{log as h}from"../log.js";import{teamsCommandLine as dn}from"./command.js";import{teamsObserver as fn}from"./context.js";import{resolveTeamsDestination as mn}from"./destination.js";import{describeTeamsFile as un,parseTeamsFile as wn,teamsAttachment as L,teamsFilePayload as yn}from"./files.js";import{landedMidConversation as pn,teamsHistory as vn}from"./history.js";import{findPeople as gn,linkMentions as bn,teamsRoster as Tn}from"./mentions.js";import{teamsPresence as In}from"./presence.js";import{teamsSighting as W}from"./principal.js";import{addressesAgent as E,routeActivity as kn}from"./route.js";import{baseConversation as On,conversationOf as N,destinationHandleOf as xn,destinationOfHandle as Sn,fileRouteOf as Y,sameConversation as An,scopeOf as z,threadIdOf as K,threadIn as M,threadKeyOf as En}from"./thread.js";function Nn(o){return[...Kn(o),...Mn].join(`
|
|
2
|
+
`)}function Kn(o){return G(o)?["This conversation is in Microsoft Teams. Each thing you write is posted as its own message","while you work, so somebody is watching the whole turn. Say what you are doing as you go,","and keep it to a line or two. Your last message is the answer to what was asked.","",ln(o)?"Your tool calls show as one message that is rewritten as you work, and replaced by a count of them once you speak again.":"Your tool calls are not shown, so a stretch of them is a silence unless you say what you are doing.","","Nothing more can reach this conversation once the turn ends, so never say you will follow up","separately: finish what you were asked before you stop.",""]:["This conversation is in Microsoft Teams. Only the last thing you write reaches it, and","anything you say on the way there is dropped, so put the whole answer in the message you","finish with. Never say you will follow up separately: nothing more can reach this","conversation until someone writes to you again.","","Nobody is watching while you work. None of your tool calls are visible and nothing you say","arrives until the turn is over, so a turn that takes minutes costs nothing, and there is no","typing indicator to say you are still going.",""]}const Mn=["Teams renders a narrower markdown than Slack does. Bold, italics, links, inline code, fenced","code blocks and lists work. Tables do not, so write a list or plain prose where you would","otherwise reach for one.","","A file somebody attaches reaches you in a direct chat and in a channel, and so does an image","pasted into a message anywhere. One Bishop could not download arrives saying why instead of","going missing, so you can tell somebody what happened to it. A group chat is the one place a","file may not reach you at all: if somebody there asks about one you were given nothing for,","say it never arrived and ask them to send it in a direct chat.","","People are named to you by their Teams display name. Write @ and the name exactly as you were",'given it, spaces included, and Bishop makes it a real mention, as in "@Kim Lee, can you take','a look?". A name that does not match exactly one person stays plain text. You can always',"mention the person whose message you are answering. @here, @channel, @everyone and team","mentions are never created, whoever writes them.","",'Asked to mention somebody by part of their name, like "mention Kim", look them up with',"teams_find_person first. If one person matches, write @ and the full name it gives. If more","than one does, ask which one was meant rather than guessing.","","A conversation can hold people talking to each other and not to you. Bishop marks a message",'you may leave alone with a <thread> tag carrying reply="optional": other people are talking',"here and this one did not @mention you, so answer it only if you have something worth adding.","To stay out of it, say <no-output> and nothing else, and Bishop posts nothing at all. Every","message without that tag is yours to answer."],_n="You are not on this agent\u2019s allow list, so it can\u2019t answer you.",U="There was no text in that message for me to act on. Say what you need in words, and @mention me so I see it.",Bn=U+" If you attached a file, nothing about it reached me, so send it in a direct chat.",Pn=2e4,Cn=3600*1e3;function Zn(o){const p=new Set,b=new Map,_=new Map,k=sn(),B=fn({api:o.api,...o.tenantId?{tenantId:o.tenantId}:{}}),g=Tn(o.api),T=hn(o.meta,"teams",o.verbosity),q={verbosity:T,model:o.model,configured:o.configuredModel??{},stop:o.stop},O=new Map;let w,P=!1,C=!1;function x(e){return{name:"teams",guidance:Nn(T.of(e.id).level),...w?{self:w}:{}}}async function D(e,n,t){if(!n)return;const i=b.get(n);if(i)return i;if((_.get(n)??0)>Date.now())return;const a=On(e);let r;try{r=await o.api.member(a,t),r&&b.set(n,r)}catch(s){h.warn({err:s,conversation:a},"could not read the Teams roster")}return!r&&o.allow.open&&_.set(n,Date.now()+Cn),r}async function y(e,n,t={}){const{files:i=[],opened:a}=t;let{channel:r}=t,s=e,c;const v=n?cn(n,Pn):[""];for(const[d,f]of v.entries()){const m=d===v.length-1,u=t.mentions?await bn(f,t.mentions):{text:f,mentions:[]},I=m&&(i.length>0||r)||u.mentions.length>0?{...m&&i.length>0?{files:i}:{},...m&&r?{channel:r}:{},...u.mentions.length>0?{mentions:u.mentions}:{}}:void 0,l=await o.api.send(s,u.text,I);a&&d===0&&l&&(a.root=l.id,s=M(e,l.id),r&&(r={...r,threadId:l.id})),c=l?{id:l.id,text:u.text,...u.mentions.length>0?{mentions:u.mentions}:{}}:void 0}return c}function F(e,n,t){const i=[n?.handle,n?.name].filter(a=>!!a);return{...i.length>0&&t?{sender:{id:t,names:i}}:{},roster:async()=>(await g.of(e))?.people}}function j(e,n){return[e,n?.userPrincipalName,n?.email].filter(t=>!!t).map(t=>t.trim().toLowerCase())}function S(e,n){return o.allow.open||j(e,n).some(t=>o.allow.allows(t))}function R(e,n){const t=yn(n);if(!t)return;const i=un(n);return o.attachments.remember(e,"teams",{id:i.id,name:i.name,payload:t})}async function Q(e,n){return n.files.length===0?[]:Promise.all(n.files.map(async t=>{const i=R(e.id,t),a=await o.files.save(e.dirKey,L(t,o.api));return i?{...a,ref:i}:a}))}function V(e,n,t){return o.principals.see(x(e).name,W({id:n.from.aadObjectId??n.from.id,...n.from.name?{name:n.from.name}:{},bot:n.from.id.startsWith("28:")},t,o.tenantId))}function H(e,n){return o.principals.see("teams",{externalId:e,...n?{handle:n,name:n}:{},kind:"bot",affiliation:"unknown",self:!0})}async function X(e,n){const t=x(e),i=N(n),a=K(i),r=n.recipientId?.replace(/^28:/,"");return vn({read:await o.api.history(n),...a?{openedBy:a}:{},...r?{selfAppId:r}:{},...w?{selfName:w.address}:{},...n.recipientId?{self:()=>H(n.recipientId,n.recipientName)}:{},sender:async s=>{const c=await D(i,s.id,s.id);return{allowed:S(s.id,c),principal:()=>o.principals.see(t.name,W({id:s.id,...s.name?{name:s.name}:{},bot:!1},c,o.tenantId))}},remember:s=>R(e.id,s),saw:(s,c)=>k.saw(e.threadKey,s,c)})}function J(e,n,t,i){const a=N(n),r=K(a),s=n.channel?{teamId:n.channel.teamId,channelId:n.channel.channelId,...r?{threadId:r}:{}}:void 0,c=rn({participants:k.count(e.threadKey),mentioned:E(n)}),v=F(a,t,n.from.id);return{thread:e,isNewThread:i,get origin(){return x(e)},...c?{note:c}:{},...pn(n)?{history:()=>X(e,n)}:{},observe:()=>B.observe({conversationId:n.conversationId,scope:z(n),...n.channel?{channel:n.channel}:{},speaker:t.affiliation}),async messages(){const d=await Q(e,n);return[{principal:t,externalId:n.id,text:n.text,...d.length>0?{files:d}:{}}]},showsProgress:()=>G(T.of(e.id).level),presence:()=>{const d=In({send:(f,m)=>y(a,f,{...m?{files:m}:{},...s?{channel:s}:{},mentions:v}),post:f=>o.api.send(a,f),update:(f,m,u)=>o.api.update(a,f,m,u),threadKey:e.threadKey,verbosity:()=>T.of(e.id).level,...Y(z(n),s)==="none"?{share:!1}:{},...c?{mayDecline:!0}:{}});return O.set(e.threadKey,d),{...d,finish:async()=>{try{await d.finish()}finally{O.delete(e.threadKey)}}}},async notify(d){await y(a,d)},async destination(){return{interface:"teams",handle:xn(n),label:Dn(n)}}}}async function Z(e){if(C)return;if(await o.events.seen(`teams:${e.id}`)){h.debug({activity:e.id},"already answered this Teams activity");return}!w&&e.recipientName&&(w={address:`@${e.recipientName}`},h.info({address:w.address},"the agent is known by this name in Teams")),!P&&e.recipientId&&(P=!0,H(e.recipientId,e.recipientName));const n=En(e),t=await o.threads.has(n),i=kn(e,{threadKnown:t});if(i.action==="ignore"){h.debug({activity:e.id,thread:n,reason:i.reason},"leaving a Teams message alone");return}const a=e.from.aadObjectId;if(!a){h.warn({activity:e.id},"refusing a Teams message with no Entra object ID");return}k.saw(n,a,Date.now());const r=N(e),s=await D(r,a,e.from.id);if(!S(a,s)){h.warn({activity:e.id,who:a,conversation:r},"refusing an unauthorized Teams message"),E(e)&&await y(r,_n).catch(l=>{h.debug({err:l},"could not tell somebody they are not on the allow list")});return}const c=dn(e);if(c){const l=await o.threads.remember(n),A=on(c,{thread:l},q);try{await y(r,A),O.get(n)?.interrupted()}catch(tn){h.warn({thread:n,err:tn},"couldn't answer a command")}h.info({thread:n,who:a},"ran a command");return}const v=new Set(e.files.map(l=>l.contentUrl)),d=(await o.api.sharedFiles(e)).filter(l=>!v.has(l.contentUrl)),f=d.length>0?{...e,files:[...e.files,...d]}:e;if(!f.text.trim()&&f.files.length===0){if(h.info({activity:e.id},"a Teams message had nothing to act on"),E(e)){const l=e.conversationType==="personal"?U:Bn;await y(r,l).catch(A=>{h.debug({err:A},"could not answer a message with nothing in it")})}return}const m=await o.threads.remember(n),u=V(m,f,s);h.info({thread:n,who:a,newThread:!t},"running a turn from Teams");const I=o.run(J(m,f,u,!t)).catch(l=>{h.error({thread:n,err:l},"a turn from Teams failed")}).finally(()=>{p.delete(I)});p.add(I)}async function $(){for(;p.size>0;)await Promise.allSettled([...p])}function nn(e){return e==="groupChat"?"Bishop cannot send files to Teams group chats yet.":e==="channel"?"Bishop has no Graph address for this Teams channel, so it cannot send files there.":"Bishop cannot send files to this older Teams destination. Ask for a new report in the conversation first."}async function en(e){const n=Sn(e.handle),t=e.files??[],i=t.map(s=>s.name).join(", ");if(t.length>0&&Y(n.scope,n.channel)==="none")throw new an(nn(n.scope));const a=n.scope==="channel"&&!K(n.conversation)?{}:void 0,r=F(n.conversation);try{await y(n.conversation,e.text,{...n.channel?{channel:n.channel}:{},mentions:r,files:t,...a?{opened:a}:{}})}catch(s){if(!t.length)throw s;h.warn({handle:e.handle,files:i,err:s},"could not attach files to a Teams report");const c=a?.root;await y(c?M(n.conversation,c):n.conversation,`${e.text}
|
|
3
3
|
|
|
4
|
-
_Could not attach ${i}; the report was sent without the file._`,{...n.channel?{channel:c?{...n.channel,threadId:c}:n.channel}:{},mentions:r,...a&&!c?{opened:a}:{}})}return a?.root?{threadKey:`teams:${M(n.conversation,a.root)}`}:{}}return{name:"teams",handle:
|
|
4
|
+
_Could not attach ${i}; the report was sent without the file._`,{...n.channel?{channel:c?{...n.channel,threadId:c}:n.channel}:{},mentions:r,...a&&!c?{opened:a}:{}})}return a?.root?{threadKey:`teams:${M(n.conversation,a.root)}`}:{}}return{name:"teams",handle:Z,changed:e=>{B.changed(e),g.changed(e.conversationId),e.channelId&&g.changed(e.channelId)},settle:$,deliver:en,allows(e){return o.allow.open?!0:[...j(e.externalId,b.get(e.externalId)),...e.email?[e.email.trim().toLowerCase()]:[]].some(t=>o.allow.allows(t))},sameConversation:An,async findPeople(e,n){const t=await g.of(e.threadKey.replace(/^teams:/,""));return t?{kind:"people",people:gn(t.people,n).map(a=>({name:a.name,...a.email?{email:a.email}:{}})),complete:t.complete}:{kind:"refused",reason:"Bishop could not read who is in this conversation just now."}},resolveDestination:(e,n)=>mn({api:o.api,roster:g,allows:t=>S(t.aadObjectId,t),nameOf:t=>b.get(t)?.name},e,n),attachment:e=>{const n=wn(e);return n?L(n,o.api):void 0},async start(){await o.listener.start(),h.info({allow:o.allow.open?"anyone in the tenant":"a list"},"listening for Teams activities")},async stop(){C=!0,await o.listener.stop(),p.size>0&&(h.info({turns:p.size},"waiting for Teams turns still running"),await $())}}}function Dn(o){return o.conversationType==="personal"?`a chat with ${o.from.name??"someone"}`:o.conversationType==="groupChat"?"a Teams group chat":"a Teams channel thread"}export{Zn as createTeamsInterface,Nn as teamsGuidance};
|
package/dist/teams/presence.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{DEFAULT_VERBOSITY as E}from"../config.js";import{saysNoOutput as K}from"../harness/principal.js";import{
|
|
1
|
+
import{DEFAULT_VERBOSITY as E}from"../config.js";import{saysNoOutput as K}from"../harness/principal.js";import{retitlePhrase as N,toolPhrase as S,turnTally as $}from"../interface/phrases.js";import{showsCost as O,showsProgress as j,showsTasks as b}from"../interface/verbosity.js";import{log as o}from"../log.js";import{fileShelf as F}from"../outgoing.js";import{teamsTask as H}from"./progress.js";const C="The agent finished without saying anything. Nothing went wrong that it reported, but there is no answer to send.";function q(e){const k=()=>e.verbosity?.()??E,A=Date.now();let h;const w=[];let c=0,i="",s,y="",D=!1,f,I=!1;const u=F();let l=!1;async function g(){const t=h;t&&(h=void 0,await t.close())}async function P(t,n=[]){await g();try{const r=await e.send(t,n);c+=1,i=t,s=r,l=n.length>0}catch(r){if(n.length>0)try{const a=await e.send(t);c+=1,i=t,s=a,l=!1,o.warn({thread:e.threadKey,files:n.map(d=>d.name),err:r},"could not attach files to a Teams message; sent the text without them");return}catch(a){o.debug({thread:e.threadKey,err:a},"could not post the Teams message without its files")}i="",s=void 0,l=!1,o.debug({thread:e.threadKey,err:r},"could not post to Teams while working")}}function _(t){return t.filter(n=>typeof n!="string")}return{...e.share===!1?{}:{share:t=>u.offer(t)},interrupted(){i="",s=void 0,l=!1},async handle(t){const n=k();switch(b(n)||await g(),t.type){case"text":{if(K(t.text))return;if(!j(n)){w.push(u.strip(t.text));return}const r=u.place(t.text),a=_(r.parts);if(!r.text.trim()&&a.length===0)return;w.push(r.text),await P(r.text,a);return}case"tool":if(!b(n))return;h||(h=H({send:e.post,update:e.update,threadKey:e.threadKey},e.taskIntervalMs),i="",s=void 0),h.note(S(t));return;case"tool_summary":if(!b(n))return;h?.retitle(N(t));return;case"done":D=!0,y=K(t.text)?"":t.text,I=!!e.mayDecline&&K(t.text);return;case"error":f=t.message;return;default:return}},async finish(){if(await g(),I&&!f){o.info({thread:e.threadKey},"the agent stayed out of a Teams conversation");return}const t=y?u.place(y):{parts:[],text:""},n=t.text.trim()||(c===0?w.join(`
|
|
2
2
|
|
|
3
3
|
`).trim():""),r=_(t.parts),a=B(n),d=O(k())?`_${$(Date.now()-A,D||!!f)}_`:"";if(!a&&d&&s&&!l)try{await e.update(s.id,`${s.text}
|
|
4
4
|
|
package/dist/turn.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createHash as
|
|
1
|
+
import{createHash as le,randomUUID as Z}from"node:crypto";import{rm as ce}from"node:fs/promises";import{join as $}from"node:path";import{contextChangeNote as he,guidanceChangeNote as fe}from"./harness/principal.js";import{mint as ee}from"./ids.js";import{log as i}from"./log.js";import{SHARE_FILE_TOOL as ge}from"./outgoing.js";import{SYSTEM_PRINCIPAL as we}from"./principals.js";import{stageFile as te}from"./sandbox.js";import{schedulePresence as ue}from"./schedule/presence.js";import{inbound as ye,outbound as oe}from"./transcript.js";import{worktreeName as me}from"./worktrees.js";function pe(o){const{location:y,visibility:_,audience:H}=o;return{...y?{location:{kind:y.kind,...y.name?{name:y.name}:{}}}:{},visibility:_,audience:H}}const Y=Symbol("not yet");function be(o){if(o)try{return JSON.parse(o)}catch{return}}function Te(o){const y=new Map,_=new Set;async function H(e){const n=o.sharedDirNotice;if(!(!n||_.has(e.thread.threadKey))){_.add(e.thread.threadKey);try{await e.notify(n)}catch(r){i.debug({thread:e.thread.threadKey,err:r},"could not deliver the shared-directory notice")}}}const P=new Map,A=new Map;async function B(e){await ce(e,{recursive:!0,force:!0}).catch(n=>i.warn({dir:e,err:n},"couldn't remove a turn's shared-file copies"))}function q(e,n){const r=A.get(e);if(r){A.delete(e),r.files.length>0&&i.warn({threadId:e,files:r.files.length,why:n},"dropping files held follow-ups shared");for(const d of r.staging)B(d)}}o.harness.keepAlive?.attach({started(e,n){P.get(e)?.lease?.revoke();const r=o.tools?.lease();return P.set(e,{processId:n,...r?{lease:r}:{}}),r?.access},ended(e,n){const r=P.get(e);r?.processId===n&&(r.lease?.revoke(),P.delete(e),q(e,"the process ended before a follow-up was posted"))},followUp(e,n,r){re(e,n,r).catch(d=>{i.error({threadId:e,err:d},"a follow-up could not be handled")})}});function z(e,n,r,d,s,g,w){const u=s.share?.bind(s),m=u&&(async c=>{const h=await u(c);return g.set(h.ref,h),h});return{thread:e,principal:d,origin:n,destination:r,...m?{share:m}:{},...w?{stage:w}:{}}}function ne(e){if(!o.tools||e.tools===!1)return!1;const n=Array.isArray(e.tools)?e.tools:void 0;return n&&n.length===0?!1:n}async function re(e,n,r){const d=P.get(e),s=d?.followable,g=s?.thread.threadKey??e,w=ee("trn"),u=ue(),m=new Map;let c,h;const l=Date.now();i.info({thread:g,turn:w},"the agent is following up on its own");const f={thread:g,turn:w};try{if(s){let a;const O=s.sandbox;if(O&&o.sandbox){const M=$(o.sandbox.staging,Z());c=M,a=S=>te(O,S,M)}h=d?.lease?.bind(z(s.thread,s.origin,s.destination,we,u,m,a),{only:[ge]})}let v;try{for await(const a of n)s&&(a.type==="text"||a.type==="done")&&o.transcript?.record([oe({threadId:s.thread.id,turnId:w,kind:a.type==="done"?"result":"progress",agent:s.agent,text:a.text,shared:m,at:Date.now()})]),a.type==="tool"&&i.debug({thread:g,tool:a.name,detail:a.summary??a.detail},"tool activity"),await u.handle(a)}catch(a){v=a instanceof Error?a.message:String(a)}h?.(),h=void 0;const p=u.outcome(),k=v??p.failure,b={...f,totalMs:Date.now()-l};if(k){i.error({...b,err:k},"the agent failed while following up");return}const N=r();if(p.silent||!p.text){i.info(b,"the agent followed up with nothing to say"),N||q(e,"the agent finished with nothing to say");return}if(s&&N&&!s.showsProgress()){const a=A.get(e)??{files:[],staging:[]};a.files.push(...p.files),c&&a.staging.push(c),c=void 0,A.set(e,a),i.info({...b,files:p.files.length},"holding the follow-up: work the agent handed off is still running");return}const K=o.deliver?.();if(!s||!K){i.warn(b,"the agent followed up, but there's nowhere to post it; dropping it");return}const E=A.get(e);A.delete(e);const U=[...E?.files??[],...p.files];let D;try{D=await s.destination(),await K(D,{text:p.text,title:"Following up",...U.length>0?{files:U}:{}})}finally{for(const a of E?.staging??[])await B(a)}i.info({...b,to:D.label},"posted the agent's follow-up")}catch(v){i.error({...f,err:v},"couldn't post the agent's follow-up")}finally{h?.(),c&&await B(c)}}async function ae(e){const n=o.principals;if(!n)return;const r=n.agent(e.origin.name);if(r)return r;try{return n.agent((await e.destination()).interface)}catch(d){i.debug({thread:e.thread.threadKey,err:d},"couldn't name the agent's account");return}}async function ie(e){await H(e);const n=new AbortController,r=ee("trn"),d=new Map,s=e.presence();let g,w,u;const m=Date.now();let c,h;try{y.set(e.thread.threadKey,n);const l=e.observe?.().catch(t=>(i.warn({thread:e.thread.threadKey,err:t},"could not read where this conversation takes place"),{visibility:"unknown",audience:"unknown"})),f=e.freshSession?void 0:await o.sessions.get(e.thread.id),v=await o.cwd(e.thread),[p,k]=await Promise.all([e.messages(),f?void 0:e.history?.().catch(t=>{i.warn({thread:e.thread.threadKey,err:t},"could not read the history")})]),b=l&&await Promise.race([l,Promise.resolve(Y)]),N=b===Y?void 0:b;b===Y&&l&&(i.debug({thread:e.thread.threadKey},"where this conversation takes place isn't read yet; not waiting for it"),l.then(t=>o.contexts?.record(e.thread.id,e.origin.name,t)).catch(t=>{i.warn({thread:e.thread.threadKey,err:t},"could not record where this conversation takes place")}));const[K,...E]=p;if(!K)throw new Error("a turn with no messages");const U=E.at(-1)?.principal??K.principal,D=o.transcript?await ae(e):void 0;if(n.signal.aborted){i.info({thread:e.thread.threadKey},"turn stopped before the agent started");return}const a=await o.sandbox?.policy(v,e.thread);let O;if(a&&o.sandbox){const t=$(o.sandbox.staging,Z());u=t,O=J=>te(a,J,t)}const M=!!o.harness.keepAlive&&!e.unattended,S=ne(e),L=S===!1?void 0:z(e.thread,e.origin,()=>e.destination(),U,s,d,O);L&&!M&&(g=o.tools?.grant(L,...S?[{only:S}]:[]));const se=()=>{if(!M||w)return;const t=P.get(e.thread.id);t&&(t.followable={thread:e.thread,origin:e.origin,destination:()=>e.destination(),...D?{agent:D}:{},...a?{sandbox:a}:{},showsProgress:()=>e.showsProgress?.()??!1},L&&(w=t.lease?.bind(L,...S?[{only:S}]:[])))};c=Date.now()-m,o.contexts?.record(e.thread.id,e.origin.name,N);const G=o.contexts?.told(e.thread.id),x=N?pe(N):l&&be(G),T=x&&JSON.stringify(x),Q=!!(f&&T&&G!==T);Q&&x&&i.info({thread:e.thread.threadKey,...x},"this conversation changed since the agent was told about it; telling it again");const C=e.origin,I=le("sha256").update(C.guidance).digest("hex"),V=o.contexts?.guided(e.thread.id),W=!!(f&&V&&V!==I);W&&i.info({thread:e.thread.threadKey,interface:C.name},"the interface's guidance changed since the agent was given it; giving it again");const R=[e.note,Q&&x?he(x):void 0,W?fe(C.guidance):void 0].filter(Boolean).join(`
|
|
2
2
|
|
|
3
|
-
`),
|
|
3
|
+
`),de=x?{...C,context:x}:C,j=o.model?.of(e.thread.id)??{};let F;const X=(t,J)=>o.transcript?.record([oe({threadId:e.thread.id,turnId:r,kind:t,agent:D,text:J,shared:d,at:Date.now()})]);o.transcript?.record(ye({threadId:e.thread.id,turnId:r,messages:[K,...E],...R?{note:R}:{},...k?.length?{history:k}:{},at:Date.now()}));for await(const t of o.harness.runTurn({messages:[K,...E],cwd:v,threadKey:e.thread.threadKey,worktree:me(e.thread.dirKey),...f?{sessionId:f}:{},...j.model?{model:j.model}:{},...j.effort?{effort:j.effort}:{},...R?{note:R}:{},...k?.length?{history:k}:{},origin:de,...g?{tools:g.access}:{},...a?{sandbox:a}:{},threadId:e.thread.id,...M?{keepAlive:M}:{},signal:n.signal}))se(),t.type==="session"&&(h??=Date.now()-m,t.model&&(F=t.model),t.sessionId!==f&&await o.sessions.set(e.thread.id,t.sessionId),T&&o.contexts?.told(e.thread.id)!==T&&o.contexts?.tell(e.thread.id,T),o.contexts?.guided(e.thread.id)!==I&&o.contexts?.guide(e.thread.id,I)),t.type==="text"&&(X("progress",t.text),i.debug({thread:e.thread.threadKey,chars:t.text.length},"agent said something")),t.type==="tool"&&i.debug({thread:e.thread.threadKey,tool:t.name,detail:t.summary??t.detail},"tool activity"),t.type==="done"&&(X("result",t.text),i.info({thread:e.thread.threadKey,turn:r,...F?{model:F}:{},...t.usage},"turn complete")),t.type==="error"&&i.error({thread:e.thread.threadKey,turn:r,...F?{model:F}:{},kind:t.kind,err:t.message},"turn errored"),await s.handle(t)}catch(l){const f=l instanceof Error?l.message:String(l);throw i.error({thread:e.thread.threadKey,turn:r,err:l},"turn failed before the agent answered"),await s.handle({type:"error",kind:"turn_failed",message:f}),l}finally{i.debug({thread:e.thread.threadKey,turn:r,...c===void 0?{}:{preparedMs:c},...h===void 0?{}:{sessionMs:h},totalMs:Date.now()-m,...n.signal.aborted?{stopped:!0}:{}},"turn timing"),g?.revoke(),w?.(),y.get(e.thread.threadKey)===n&&y.delete(e.thread.threadKey),await s.finish(),u&&await B(u)}}return{run:ie,stop(e){const n=y.get(e);return n?(n.abort(),!0):!1}}}export{Te as makeTurnRunner};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentdeploymentco/bishop",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "An agent gateway. Connects an agent to Slack, email, and Microsoft Teams.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -49,13 +49,15 @@
|
|
|
49
49
|
"@microsoft/teams.apps": "^2.0.16",
|
|
50
50
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
51
51
|
"@openai/codex-sdk": "0.164.0",
|
|
52
|
-
"@slack/bolt": "^5.
|
|
53
|
-
"@slack/
|
|
52
|
+
"@slack/bolt": "^5.1.0",
|
|
53
|
+
"@slack/types": "^3.2.0",
|
|
54
|
+
"@slack/web-api": "^8.2.0",
|
|
54
55
|
"commander": "^13",
|
|
55
56
|
"cron-parser": "^5.10.0",
|
|
56
57
|
"dotenv": "^17",
|
|
57
58
|
"luxon": "^3.7.2",
|
|
58
59
|
"pino": "^9",
|
|
60
|
+
"undici": "^8.12.3",
|
|
59
61
|
"zod": "^4"
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|