@miadi/connector 0.1.22 → 0.1.23

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/approve.d.ts CHANGED
@@ -66,6 +66,7 @@ export declare function runBrief(record: ConnectorRecord, approver: MiadiPerson,
66
66
  aspect: string;
67
67
  name: string;
68
68
  ask: string;
69
+ settled_by?: string;
69
70
  }[]): {
70
71
  to: string;
71
72
  brief: string;
package/dist/approve.js CHANGED
@@ -147,7 +147,7 @@ export function runBrief(record, approver, sessions, instructions, providerAttrs
147
147
  : []),
148
148
  // A person may approve past an open question: the worker reads it, and says in its result what it could not settle (jgwill/Miadi#939).
149
149
  ...(openAspects.length
150
- ? ["", "These questions were open against the mission standard when it was approved. Settle what reading can settle, and say in your result what you could not:", "", "<open_questions>", ...openAspects.map((f) => text(`${f.aspect} ${f.name}: ${f.ask}`)), "</open_questions>"]
150
+ ? ["", "These questions were open against the mission standard when it was approved. Settle what reading can settle, and say in your result what you could not:", "", "<open_questions>", ...openAspects.map((f) => text(`${f.aspect} ${f.name}: ${f.ask}${f.settled_by === "person" ? " Only the person can answer this: ask them, and do not assume it." : ""}`)), "</open_questions>"]
151
151
  : []),
152
152
  ...(instructions
153
153
  ? [
@@ -956,7 +956,12 @@ function draftReviewSection(r, review, canAnswer = false) {
956
956
  .join("");
957
957
  const openCodes = new Set(open.map((f) => f.aspect));
958
958
  const unchecked = review.not_checked.filter((n) => !openCodes.has(n.aspect));
959
- const uncheckedLine = unchecked.length ? stateLine("Read it yourself", EYE_PATHS, `No check covers these, so read the mission for them: ${unchecked.map((n) => `<span class="code">${escapeHtml(n.aspect)}</span> ${escapeHtml(n.name)}`).join(" · ")}`) : "";
959
+ const partly = unchecked.filter((n) => n.in_part);
960
+ const uncovered = unchecked.filter((n) => !n.in_part);
961
+ const uncheckedLine = [
962
+ ...partly.map((n) => stateLine("Checked in part", EYE_PATHS, `Checked in part: <span class="code">${escapeHtml(n.aspect)}</span> ${escapeHtml(n.name)} (${escapeHtml(n.in_part)})`)),
963
+ ...(uncovered.length ? [stateLine("Read it yourself", EYE_PATHS, `No check covers these, so read the mission for them: ${uncovered.map((n) => `<span class="code">${escapeHtml(n.aspect)}</span> ${escapeHtml(n.name)}`).join(" · ")}`)] : []),
964
+ ].join("\n");
960
965
  const heading = open.length ? `${open.length} open on the mission standard v${review.standard.version}` : `The mission standard v${review.standard.version}`;
961
966
  return `<section class="standard" aria-label="The mission standard">
962
967
  <h2>${escapeHtml(heading)}</h2>
@@ -19,4 +19,4 @@ export declare class DecisionTokens {
19
19
  * and calls `decide_request` through the host. In a host without that bridge it
20
20
  * shows each request's approval page instead.
21
21
  */
22
- export declare const DECISIONS_CARD_HTML = "<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<style>\n:root{color-scheme:light dark;--fg:#1d1b18;--muted:#5b5650;--line:#d9d4cc;--accent:#7a4a00;--ok:#1d6b3a;--err:#b00020;--chip:#f1ede6}\n@media (prefers-color-scheme:dark){:root{--fg:#ece8e1;--muted:#b3ada4;--line:#3a3631;--accent:#ffd700;--ok:#7fd49b;--err:#ff8a80;--chip:#24211d}}\n*{box-sizing:border-box}\nbody{margin:0;color:var(--fg);font:15px/1.45 system-ui,sans-serif;background:transparent}\nmain{padding:12px;display:grid;gap:12px}\n.empty,.note{color:var(--muted)}\narticle{border:1px solid var(--line);border-radius:10px;padding:12px}\nh2{font-size:15px;margin:0 0 4px}\n.kind{display:inline-block;font-size:12px;padding:1px 8px;border-radius:999px;background:var(--chip);color:var(--muted);margin-bottom:6px}\n.meta{font-size:13px;color:var(--muted);margin:0 0 6px}\n.does{margin:6px 0;font-weight:600}\ndetails{margin:6px 0}\nsummary{cursor:pointer;color:var(--muted);font-size:13px}\npre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px;margin:6px 0 0;max-height:240px;overflow:auto}\ntextarea{width:100%;min-height:64px;padding:8px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--fg);font:inherit;margin-top:8px}\n.row{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}\nbutton{padding:8px 12px;border-radius:8px;border:1px solid var(--line);background:transparent;color:var(--fg);font:inherit;cursor:pointer;min-height:40px}\nbutton.primary{border-color:var(--accent);outline:1px solid var(--accent)}\nbutton:focus-visible,a:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\nbutton[disabled]{opacity:.55;cursor:default}\na{color:inherit}\n.done{color:var(--ok);font-weight:600}\n.std{border-top:1px solid var(--line);margin:8px 0;padding-top:8px;display:grid;gap:8px}\nh3{font-size:14px;margin:0;font-weight:600}\n.aspects{list-style:none;margin:4px 0;padding:0;display:grid;gap:14px}\n.asp{display:grid;grid-template-columns:16px 1fr;gap:6px;font-size:14px;overflow-wrap:anywhere}\n.asp .gl{margin-top:3px;color:var(--muted);display:block}\n.asp p{margin:1px 0}\n.asp .nm{color:var(--muted)}\n.asp .ask{font-weight:600}\n.asp details{margin:0}\n.asp details[open]{border-left:2px solid var(--accent);padding-left:8px;margin-top:2px}\n.asp summary{padding:8px 0}\n.who{display:flex;gap:8px;align-items:center}\n.who .settler{flex:1}\n.yours .settler{color:var(--fg);font-weight:600}\n.yours .gl{color:var(--accent)}\n.iconbtn{min-height:34px;width:34px;padding:0;display:inline-grid;place-items:center;flex:none;border-color:var(--accent);color:var(--accent)}\n.openL{display:none}\ndetails[open]>summary .openL{display:inline}\ndetails[open]>summary .closedL{display:none}\n.fact{margin:0;font-size:14px;overflow-wrap:anywhere}\n.code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}\n.small{font-size:13px;color:var(--muted);margin:0;overflow-wrap:anywhere}\nblockquote{margin:4px 0 0;padding-left:8px;border-left:2px solid var(--line);font-size:14px;overflow-wrap:anywhere}\nsummary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n</style></head><body><main id=\"root\" aria-live=\"polite\"><p class=\"note\">Loading requests\u2026</p></main>\n<script>\n(function(){\n var root=document.getElementById(\"root\");\n var decided={};\n // A redraw while a decision is being recorded must not offer the buttons again.\n var pending={};\n var errors={};\n // A decision still running is followed to its outcome (jgwill/Miadi#924): one read every POLL_MS, at most POLL_MAX.\n var POLL_MS=3000,POLL_MAX=100,polls={};\n // An error that leaves the request open offers its approval page under it.\n var relink={};\n function el(tag,attrs,text){var e=document.createElement(tag);if(attrs)for(var k in attrs){if(k===\"class\")e.className=attrs[k];else e.setAttribute(k,attrs[k]);}if(text!=null)e.textContent=text;return e;}\n function host(){return window.openai||null;}\n function data(){var h=host();return h&&h.toolOutput?h.toolOutput:null;}\n function tokens(){var h=host();var m=h&&h.toolResponseMetadata;return (m&&m[\"miadi/decisionTokens\"])||{};}\n function render(){\n var d=data();root.textContent=\"\";\n if(!d){root.appendChild(el(\"p\",{class:\"note\"},\"Waiting for the request list.\"));return;}\n var list=d.decisions||[];\n if(!list.length){root.appendChild(el(\"p\",{class:\"empty\"},d.message||\"No request is waiting for a decision.\"));return;}\n if(!d.can_decide_here&&d.reason){root.appendChild(el(\"p\",{class:\"note\"},d.reason));}\n var t=tokens();\n list.forEach(function(r){root.appendChild(card(r,t[r.id]));});\n }\n // Where a request stands, as the connector projected it: the label, its lines, and the review it produced.\n function decidedBlock(d){\n var box=el(\"div\",{role:\"status\",class:d.cls});\n if(d.note)box.appendChild(el(\"p\",{class:\"line\"},d.note));\n box.appendChild(el(\"p\",{class:\"label\"},d.label));\n (d.lines||[]).forEach(function(l){box.appendChild(el(\"p\",{class:\"line\"},l));});\n if(d.url){var p=el(\"p\",{class:\"line\"});p.appendChild(el(\"a\",{href:d.url,target:\"_blank\",rel:\"noopener\"},d.url));box.appendChild(p);}\n return box;\n }\n function toolResult(res){\n var out=(res&&(res.structuredContent||res.structured_content))||null;\n if(!out&&res&&res.content&&res.content[0]&&res.content[0].text){try{out=JSON.parse(res.content[0].text);}catch(e){out={message:res.content[0].text};}}\n return out;\n }\n function projectionView(p,note){return {cls:p.stage===\"failed\"?\"fail\":[\"recording\",\"dispatching\"].indexOf(p.stage)!==-1?\"note\":\"done\",stage:p.stage,label:p.label,lines:p.lines||[],url:p.receipt_url||null,note:note||null};}\n // Read where a request stands. Still running: read again later. Never decided (the call was lost on the way): offer the decision again.\n function follow(r,lost){\n if(!host()||!host().callTool)return;\n polls[r.id]=(polls[r.id]||0)+1;\n host().callTool(\"get_run_result\",{record_id:r.id}).then(function(res){\n var out=toolResult(res);var p=out&&(out.projection||(out.data&&out.data.projection));\n if(!p){if(lost){delete decided[r.id];errors[r.id]=lost;}render();return;}\n if(p.stage===\"awaiting_decision\"){delete decided[r.id];if(lost)errors[r.id]=lost+\" Nothing was decided; decide again.\";render();return;}\n var note=decided[r.id]&&decided[r.id].note;\n decided[r.id]=projectionView(p,note);delete errors[r.id];render();\n if(p.stage===\"recording\"&&polls[r.id]<POLL_MAX)setTimeout(function(){follow(r);},POLL_MS);\n },function(err){\n if(lost){decided[r.id]={cls:\"note\",stage:\"recording\",label:\"Your decision may be recorded. Checking again\u2026\",lines:[],url:null,note:null};render();}\n if(polls[r.id]<POLL_MAX)setTimeout(function(){follow(r,lost);},POLL_MS);\n });\n }\n var SETTLED={drafter:\"The drafting agent can answer this.\",person:\"Only you can answer this. Write it under Instructions.\",bystander:\"Only the person deciding can answer this, in their instructions.\",local_agent:\"A local agent can settle this from the running system.\"};\n var ARROW='<svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17V3\"/><path d=\"m6 11 6 6 6-6\"/><path d=\"M19 21H5\"/></svg>';\n var DASHED='<path d=\"M10.1 2.182a10 10 0 0 1 3.8 0\"/><path d=\"M13.9 21.818a10 10 0 0 1-3.8 0\"/><path d=\"M17.609 3.721a10 10 0 0 1 2.69 2.7\"/><path d=\"M2.182 13.9a10 10 0 0 1 0-3.8\"/><path d=\"M20.279 17.609a10 10 0 0 1-2.7 2.69\"/><path d=\"M21.818 10.1a10 10 0 0 1 0 3.8\"/><path d=\"M3.721 6.391a10 10 0 0 1 2.7-2.69\"/><path d=\"M6.391 20.279a10 10 0 0 1-2.69-2.7\"/>';var EYE='<path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>';\n function glyph(label,paths){return '<svg role=\"img\" aria-label=\"'+label+'\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\">'+paths+'</svg>';}\n // A redraw rebuilds the card: what the person typed and which reasons they opened stay.\n var typed={};var whyOpen={};var boxes={};\n function stateLine(label,paths,build){var row=el(\"div\",{class:\"asp state\"});var g=el(\"span\",{class:\"gl\"});g.innerHTML=glyph(label,paths);row.appendChild(g);var t=el(\"p\",{class:\"small\"});build(t);row.appendChild(t);return row;}\n // The mission standard's open aspects, for the person deciding: what only they can answer first, and no verdict (jgwill/Miadi#939).\n function standardBlock(r,token){\n var rv=r.draft_review;\n var canAnswer=Boolean(token&&host()&&host().callTool);\n var s=el(\"section\",{class:\"std\",\"aria-label\":\"The mission standard\"});\n var list=rv?(rv.needs_revision||[]).slice().sort(function(a,b){var p=(b.settled_by===\"person\"?1:0)-(a.settled_by===\"person\"?1:0);return p||String(a.aspect).localeCompare(String(b.aspect));}):[];\n var version=rv&&rv.standard?\" v\"+rv.standard.version:\"\";\n s.appendChild(el(\"h3\",null,list.length?list.length+\" open on the mission standard\"+version:\"The mission standard\"+version));\n if(r.delivery){\n var place=el(\"p\",{class:\"fact\"});place.appendChild(el(\"span\",{class:\"small\"},\"The result lands at: \"));var disputed=list.some(function(f){return f.aspect===\"S1\";});\n place.appendChild(el(\"span\",disputed?{class:\"small\"}:null,r.delivery));\n if(disputed)place.appendChild(el(\"span\",{class:\"small\"},\" (S1 below asks about this)\"));\n s.appendChild(place);\n }\n if(r.person_words){var w=el(\"div\");w.appendChild(el(\"p\",{class:\"small\"},\"The person's words, as the drafting agent quoted them:\"));w.appendChild(el(\"blockquote\",null,r.person_words));s.appendChild(w);}\n if(!rv){s.appendChild(stateLine(\"Read it yourself\",EYE,function(t){t.textContent=\"No check ran on this version of the mission. Read it yourself.\";}));return s;}\n if(!list.length)s.appendChild(stateLine(\"Nothing flagged\",DASHED,function(t){t.textContent=\"Nothing flagged on the aspects checked.\";}));\n else{\n var ul=el(\"ul\",{class:\"aspects\"});\n list.forEach(function(f){\n var mine=f.settled_by===\"person\"&&canAnswer;\n var row=el(\"li\",{class:\"asp\"+(mine?\" yours\":\"\")});\n var g=el(\"span\",{class:\"gl\"});g.innerHTML=glyph(\"Open aspect\",DASHED);row.appendChild(g);\n var t=el(\"div\");\n var name=el(\"p\",{class:\"nm\"});name.appendChild(el(\"span\",{class:\"code\"},f.aspect));name.appendChild(el(\"span\",null,\" \"+f.name));t.appendChild(name);\n t.appendChild(el(\"p\",{class:\"ask\"},f.ask));\n if(f.span&&f.span!==r.delivery)t.appendChild(el(\"p\",{class:\"small\"},\"The mission says: \"+f.span));\n if(f.note)t.appendChild(el(\"p\",{class:\"small\"},f.note));\n var who=el(\"div\",{class:\"who\"});\n who.appendChild(el(\"p\",{class:\"small settler\"},(f.settled_by===\"person\"&&!canAnswer?SETTLED.bystander:SETTLED[f.settled_by])||f.settled_by));\n if(mine){\n var go=el(\"button\",{type:\"button\",class:\"iconbtn\",\"aria-label\":\"Go to Instructions\",title:\"Go to Instructions\"});go.innerHTML=ARROW;\n go.addEventListener(\"click\",function(){var box=boxes[r.id];if(box){if(box.scrollIntoView)box.scrollIntoView({block:\"center\"});box.focus();}});\n who.appendChild(go);\n }\n t.appendChild(who);\n var key=r.id+\"/\"+f.aspect;\n var why=el(\"details\");if(whyOpen[key])why.setAttribute(\"open\",\"\");\n why.addEventListener(\"toggle\",function(){whyOpen[key]=why.open;});\n var sum=el(\"summary\");sum.appendChild(el(\"span\",{class:\"closedL\"},\"Why\"));sum.appendChild(el(\"span\",{class:\"openL\"},\"Hide why\"));why.appendChild(sum);\n why.appendChild(el(\"p\",{class:\"small\"},f.why));\n var by=String(f.checked_by||\"\").split(\" \")[0]===\"jev\"?\"Jev\":f.checked_by===\"code\"?\"a check in code\":\"the \"+f.checked_by;\n why.appendChild(el(\"p\",{class:\"small\"},\"Flagged by \"+by+\".\"));\n t.appendChild(why);\n row.appendChild(t);ul.appendChild(row);\n });\n s.appendChild(ul);\n }\n var open={};list.forEach(function(f){open[f.aspect]=true;});\n var rest=(rv.not_checked||[]).filter(function(n){return !open[n.aspect];});\n if(rest.length)s.appendChild(stateLine(\"Read it yourself\",EYE,function(t){\n t.appendChild(el(\"span\",null,\"No check covers these, so read the mission for them: \"));\n rest.forEach(function(n,i){if(i)t.appendChild(el(\"span\",null,\" \u00B7 \"));t.appendChild(el(\"span\",{class:\"code\"},n.aspect));t.appendChild(el(\"span\",null,\" \"+n.name));});\n }));\n if(canAnswer&&list.some(function(f){return f.settled_by===\"drafter\";}))s.appendChild(el(\"p\",{class:\"small\"},\"To put the drafting agent's questions to it, say what to revise under Instructions and send it back.\"));\n if(canAnswer&&list.length)s.appendChild(el(\"p\",{class:\"small\"},\"Approving as it stands starts the session, and the worker reads these questions with the task.\"));\n return s;\n }\n function card(r,token){\n var a=el(\"article\",{\"aria-label\":r.title});\n a.appendChild(el(\"span\",{class:\"kind\"},r.kind_label||r.kind));\n a.appendChild(el(\"h2\",null,r.title));\n a.appendChild(el(\"p\",{class:\"meta\"},\"Asked by \"+r.by+(r.via?\" through \"+r.via:\"\")+\" \u00B7 \"+new Date(r.created_at).toLocaleString()+(r.circle?\" \u00B7 \"+r.circle+\" circle\":\"\")));\n a.appendChild(el(\"p\",{class:\"does\"},r.consequence));\n if(r.intention)a.appendChild(el(\"p\",null,r.intention));\n if(!decided[r.id]&&!pending[r.id]&&r.projection&&r.projection.stage===\"awaiting_decision\"&&(r.kind===\"run_request\"||r.kind===\"work_proposal\"||r.draft_review||r.delivery||r.person_words))a.appendChild(standardBlock(r,token));\n var det=el(\"details\");det.appendChild(el(\"summary\",null,\"What exactly is asked\"));det.appendChild(el(\"pre\",null,r.detail||\"\"));a.appendChild(det);\n var status=el(\"p\",{role:\"status\"});\n if(r.projection&&r.projection.stage!==\"awaiting_decision\"&&(!decided[r.id]||[\"recording\",\"dispatching\"].indexOf(r.projection.stage)===-1)){decided[r.id]=projectionView(r.projection);}\n if(decided[r.id]){\n var shown=decided[r.id];a.appendChild(decidedBlock(shown));\n if(shown.stage===\"recording\"&&polls[r.id]===undefined&&host()&&host().callTool){polls[r.id]=0;setTimeout(function(){follow(r);},POLL_MS);}\n if([\"recording\",\"dispatching\"].indexOf(shown.stage)!==-1&&host()&&host().callTool){\n var refresh=el(\"button\",{type:\"button\"},\"Refresh status\");\n refresh.addEventListener(\"click\",function(){\n refresh.disabled=true;\n host().callTool(\"get_run_result\",{record_id:r.id}).then(function(res){\n var out=toolResult(res);var p=out&&(out.projection||(out.data&&out.data.projection));\n if(p){decided[r.id]=projectionView(p);delete errors[r.id];}\n else{errors[r.id]=(out&&out.message)||\"Status could not be refreshed.\";}\n render();\n },function(err){errors[r.id]=\"Status could not be refreshed: \"+String(err&&err.message||err);render();});\n });\n a.appendChild(refresh);\n }\n if(errors[r.id])a.appendChild(el(\"p\",{class:\"fail\",role:\"status\"},errors[r.id]));\n return a;\n }\n function approvalLink(){var p=el(\"p\",{class:\"note\"});p.appendChild(document.createTextNode(\"Decide on its approval page: \"));p.appendChild(el(\"a\",{href:r.approval_url,target:\"_blank\",rel:\"noopener\"},\"open the approval page\"));return p;}\n if(pending[r.id]){status.className=\"note\";status.textContent=\"Recording your decision\u2026\";a.appendChild(status);return a;}\n if(!token||!host()||!host().callTool){a.appendChild(approvalLink());return a;}\n var ta=el(\"textarea\",{\"aria-label\":\"Instructions or changes (needed to approve with changes or send back)\",placeholder:\"Instructions or changes (needed to approve with changes or send back)\"});\n ta.value=typed[r.id]||\"\";boxes[r.id]=ta;\n ta.addEventListener(\"input\",function(){typed[r.id]=ta.value;});\n a.appendChild(ta);\n var row=el(\"div\",{class:\"row\"});\n // The ring on a mission would move with its report and read as a verdict, so a mission's buttons carry none (jgwill/Miadi#939).\n var mission=r.kind===\"run_request\"||r.kind===\"work_proposal\";\n var buttons=[[\"approve\",\"Approve\",mission?\"\":\"primary\"],[\"approve_with_changes\",\"Approve with my changes\",\"\"],[\"request_revision\",\"Send back for revision\",\"\"],[\"decline\",\"Decline\",\"\"]];\n var btns=[];\n buttons.forEach(function(b){var x=el(\"button\",{type:\"button\",class:b[2]},b[1]);x.addEventListener(\"click\",function(){go(b[0]);});btns.push(x);row.appendChild(x);});\n a.appendChild(row);a.appendChild(status);\n if(errors[r.id]){status.className=\"fail\";status.textContent=errors[r.id];if(relink[r.id])a.appendChild(approvalLink());}\n function go(choice){\n var text=ta.value.trim();\n if((choice===\"approve_with_changes\"||choice===\"request_revision\")&&!text){status.className=\"fail\";status.textContent=choice===\"request_revision\"?\"Say what to revise.\":\"Write the changes to apply.\";ta.focus();return;}\n btns.forEach(function(x){x.disabled=true;});status.className=\"note\";status.textContent=\"Recording your decision\u2026\";pending[r.id]=true;delete errors[r.id];\n host().callTool(\"decide_request\",{record_id:r.id,decision:choice,instructions:text||undefined,decision_token:token}).then(function(res){\n var out=toolResult(res);\n // The connector answers with the request's projection, also when a click finds it decided already.\n var proj=out&&(out.projection||(out.detail&&out.detail.projection));\n var code=out&&out.error;\n delete pending[r.id];\n // The token expired or the request was revised, and it still awaits a decision: say so and keep it open.\n if(code&&(!proj||proj.stage===\"awaiting_decision\")){errors[r.id]=(out&&out.message)||\"Not recorded.\";relink[r.id]=true;render();return;}\n var note=out&&out.replayed?\"This decision was already recorded. Nothing ran again.\":code===\"decision_conflict\"?\"A different decision was recorded first. Here is where it stands.\":code===\"already_decided\"?\"This request was already decided. Here is where it stands.\":null;\n if(proj){decided[r.id]=projectionView(proj,note);}\n else{decided[r.id]={cls:code?\"fail\":\"done\",label:code?((out&&out.message)||\"Not recorded.\"):((out&&out.summary)||\"Recorded.\"),lines:[],url:null,note:null};}\n render();\n },function(err){\n // The host gave up waiting; the connector may still have recorded it. Read before offering the buttons again (#924).\n delete pending[r.id];\n decided[r.id]={cls:\"note\",stage:\"recording\",label:\"Checking whether your decision was recorded\u2026\",lines:[],url:null,note:null};\n polls[r.id]=0;render();\n follow(r,\"Not recorded: \"+(err&&err.message?err.message:String(err))+\".\");\n });\n }\n return a;\n }\n window.addEventListener(\"openai:set_globals\",render);\n render();\n})();\n</script></body></html>";
22
+ export declare const DECISIONS_CARD_HTML = "<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<style>\n:root{color-scheme:light dark;--fg:#1d1b18;--muted:#5b5650;--line:#d9d4cc;--accent:#7a4a00;--ok:#1d6b3a;--err:#b00020;--chip:#f1ede6}\n@media (prefers-color-scheme:dark){:root{--fg:#ece8e1;--muted:#b3ada4;--line:#3a3631;--accent:#ffd700;--ok:#7fd49b;--err:#ff8a80;--chip:#24211d}}\n*{box-sizing:border-box}\nbody{margin:0;color:var(--fg);font:15px/1.45 system-ui,sans-serif;background:transparent}\nmain{padding:12px;display:grid;gap:12px}\n.empty,.note{color:var(--muted)}\narticle{border:1px solid var(--line);border-radius:10px;padding:12px}\nh2{font-size:15px;margin:0 0 4px}\n.kind{display:inline-block;font-size:12px;padding:1px 8px;border-radius:999px;background:var(--chip);color:var(--muted);margin-bottom:6px}\n.meta{font-size:13px;color:var(--muted);margin:0 0 6px}\n.does{margin:6px 0;font-weight:600}\ndetails{margin:6px 0}\nsummary{cursor:pointer;color:var(--muted);font-size:13px}\npre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px;margin:6px 0 0;max-height:240px;overflow:auto}\ntextarea{width:100%;min-height:64px;padding:8px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--fg);font:inherit;margin-top:8px}\n.row{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}\nbutton{padding:8px 12px;border-radius:8px;border:1px solid var(--line);background:transparent;color:var(--fg);font:inherit;cursor:pointer;min-height:40px}\nbutton.primary{border-color:var(--accent);outline:1px solid var(--accent)}\nbutton:focus-visible,a:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\nbutton[disabled]{opacity:.55;cursor:default}\na{color:inherit}\n.done{color:var(--ok);font-weight:600}\n.std{border-top:1px solid var(--line);margin:8px 0;padding-top:8px;display:grid;gap:8px}\nh3{font-size:14px;margin:0;font-weight:600}\n.aspects{list-style:none;margin:4px 0;padding:0;display:grid;gap:14px}\n.asp{display:grid;grid-template-columns:16px 1fr;gap:6px;font-size:14px;overflow-wrap:anywhere}\n.asp .gl{margin-top:3px;color:var(--muted);display:block}\n.asp p{margin:1px 0}\n.asp .nm{color:var(--muted)}\n.asp .ask{font-weight:600}\n.asp details{margin:0}\n.asp details[open]{border-left:2px solid var(--accent);padding-left:8px;margin-top:2px}\n.asp summary{padding:8px 0}\n.who{display:flex;gap:8px;align-items:center}\n.who .settler{flex:1}\n.yours .settler{color:var(--fg);font-weight:600}\n.yours .gl{color:var(--accent)}\n.iconbtn{min-height:34px;width:34px;padding:0;display:inline-grid;place-items:center;flex:none;border-color:var(--accent);color:var(--accent)}\n.openL{display:none}\ndetails[open]>summary .openL{display:inline}\ndetails[open]>summary .closedL{display:none}\n.fact{margin:0;font-size:14px;overflow-wrap:anywhere}\n.code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}\n.small{font-size:13px;color:var(--muted);margin:0;overflow-wrap:anywhere}\nblockquote{margin:4px 0 0;padding-left:8px;border-left:2px solid var(--line);font-size:14px;overflow-wrap:anywhere}\nsummary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n</style></head><body><main id=\"root\" aria-live=\"polite\"><p class=\"note\">Loading requests\u2026</p></main>\n<script>\n(function(){\n var root=document.getElementById(\"root\");\n var decided={};\n // A redraw while a decision is being recorded must not offer the buttons again.\n var pending={};\n var errors={};\n // A decision still running is followed to its outcome (jgwill/Miadi#924): one read every POLL_MS, at most POLL_MAX.\n var POLL_MS=3000,POLL_MAX=100,polls={};\n // An error that leaves the request open offers its approval page under it.\n var relink={};\n function el(tag,attrs,text){var e=document.createElement(tag);if(attrs)for(var k in attrs){if(k===\"class\")e.className=attrs[k];else e.setAttribute(k,attrs[k]);}if(text!=null)e.textContent=text;return e;}\n function host(){return window.openai||null;}\n function data(){var h=host();return h&&h.toolOutput?h.toolOutput:null;}\n function tokens(){var h=host();var m=h&&h.toolResponseMetadata;return (m&&m[\"miadi/decisionTokens\"])||{};}\n function render(){\n var d=data();root.textContent=\"\";\n if(!d){root.appendChild(el(\"p\",{class:\"note\"},\"Waiting for the request list.\"));return;}\n var list=d.decisions||[];\n if(!list.length){root.appendChild(el(\"p\",{class:\"empty\"},d.message||\"No request is waiting for a decision.\"));return;}\n if(!d.can_decide_here&&d.reason){root.appendChild(el(\"p\",{class:\"note\"},d.reason));}\n var t=tokens();\n list.forEach(function(r){root.appendChild(card(r,t[r.id]));});\n }\n // Where a request stands, as the connector projected it: the label, its lines, and the review it produced.\n function decidedBlock(d){\n var box=el(\"div\",{role:\"status\",class:d.cls});\n if(d.note)box.appendChild(el(\"p\",{class:\"line\"},d.note));\n box.appendChild(el(\"p\",{class:\"label\"},d.label));\n (d.lines||[]).forEach(function(l){box.appendChild(el(\"p\",{class:\"line\"},l));});\n if(d.url){var p=el(\"p\",{class:\"line\"});p.appendChild(el(\"a\",{href:d.url,target:\"_blank\",rel:\"noopener\"},d.url));box.appendChild(p);}\n return box;\n }\n function toolResult(res){\n var out=(res&&(res.structuredContent||res.structured_content))||null;\n if(!out&&res&&res.content&&res.content[0]&&res.content[0].text){try{out=JSON.parse(res.content[0].text);}catch(e){out={message:res.content[0].text};}}\n return out;\n }\n function projectionView(p,note){return {cls:p.stage===\"failed\"?\"fail\":[\"recording\",\"dispatching\"].indexOf(p.stage)!==-1?\"note\":\"done\",stage:p.stage,label:p.label,lines:p.lines||[],url:p.receipt_url||null,note:note||null};}\n // Read where a request stands. Still running: read again later. Never decided (the call was lost on the way): offer the decision again.\n function follow(r,lost){\n if(!host()||!host().callTool)return;\n polls[r.id]=(polls[r.id]||0)+1;\n host().callTool(\"get_run_result\",{record_id:r.id}).then(function(res){\n var out=toolResult(res);var p=out&&(out.projection||(out.data&&out.data.projection));\n if(!p){if(lost){delete decided[r.id];errors[r.id]=lost;}render();return;}\n if(p.stage===\"awaiting_decision\"){delete decided[r.id];if(lost)errors[r.id]=lost+\" Nothing was decided; decide again.\";render();return;}\n var note=decided[r.id]&&decided[r.id].note;\n decided[r.id]=projectionView(p,note);delete errors[r.id];render();\n if(p.stage===\"recording\"&&polls[r.id]<POLL_MAX)setTimeout(function(){follow(r);},POLL_MS);\n },function(err){\n if(lost){decided[r.id]={cls:\"note\",stage:\"recording\",label:\"Your decision may be recorded. Checking again\u2026\",lines:[],url:null,note:null};render();}\n if(polls[r.id]<POLL_MAX)setTimeout(function(){follow(r,lost);},POLL_MS);\n });\n }\n var SETTLED={drafter:\"The drafting agent can answer this.\",person:\"Only you can answer this. Write it under Instructions.\",bystander:\"Only the person deciding can answer this, in their instructions.\",local_agent:\"A local agent can settle this from the running system.\"};\n var ARROW='<svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 17V3\"/><path d=\"m6 11 6 6 6-6\"/><path d=\"M19 21H5\"/></svg>';\n var DASHED='<path d=\"M10.1 2.182a10 10 0 0 1 3.8 0\"/><path d=\"M13.9 21.818a10 10 0 0 1-3.8 0\"/><path d=\"M17.609 3.721a10 10 0 0 1 2.69 2.7\"/><path d=\"M2.182 13.9a10 10 0 0 1 0-3.8\"/><path d=\"M20.279 17.609a10 10 0 0 1-2.7 2.69\"/><path d=\"M21.818 10.1a10 10 0 0 1 0 3.8\"/><path d=\"M3.721 6.391a10 10 0 0 1 2.7-2.69\"/><path d=\"M6.391 20.279a10 10 0 0 1-2.69-2.7\"/>';var EYE='<path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>';\n function glyph(label,paths){return '<svg role=\"img\" aria-label=\"'+label+'\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\">'+paths+'</svg>';}\n // A redraw rebuilds the card: what the person typed and which reasons they opened stay.\n var typed={};var whyOpen={};var boxes={};\n function stateLine(label,paths,build){var row=el(\"div\",{class:\"asp state\"});var g=el(\"span\",{class:\"gl\"});g.innerHTML=glyph(label,paths);row.appendChild(g);var t=el(\"p\",{class:\"small\"});build(t);row.appendChild(t);return row;}\n // The mission standard's open aspects, for the person deciding: what only they can answer first, and no verdict (jgwill/Miadi#939).\n function standardBlock(r,token){\n var rv=r.draft_review;\n var canAnswer=Boolean(token&&host()&&host().callTool);\n var s=el(\"section\",{class:\"std\",\"aria-label\":\"The mission standard\"});\n var list=rv?(rv.needs_revision||[]).slice().sort(function(a,b){var p=(b.settled_by===\"person\"?1:0)-(a.settled_by===\"person\"?1:0);return p||String(a.aspect).localeCompare(String(b.aspect));}):[];\n var version=rv&&rv.standard?\" v\"+rv.standard.version:\"\";\n s.appendChild(el(\"h3\",null,list.length?list.length+\" open on the mission standard\"+version:\"The mission standard\"+version));\n if(r.delivery){\n var place=el(\"p\",{class:\"fact\"});place.appendChild(el(\"span\",{class:\"small\"},\"The result lands at: \"));var disputed=list.some(function(f){return f.aspect===\"S1\";});\n place.appendChild(el(\"span\",disputed?{class:\"small\"}:null,r.delivery));\n if(disputed)place.appendChild(el(\"span\",{class:\"small\"},\" (S1 below asks about this)\"));\n s.appendChild(place);\n }\n if(r.person_words){var w=el(\"div\");w.appendChild(el(\"p\",{class:\"small\"},\"The person's words, as the drafting agent quoted them:\"));w.appendChild(el(\"blockquote\",null,r.person_words));s.appendChild(w);}\n if(!rv){s.appendChild(stateLine(\"Read it yourself\",EYE,function(t){t.textContent=\"No check ran on this version of the mission. Read it yourself.\";}));return s;}\n if(!list.length)s.appendChild(stateLine(\"Nothing flagged\",DASHED,function(t){t.textContent=\"Nothing flagged on the aspects checked.\";}));\n else{\n var ul=el(\"ul\",{class:\"aspects\"});\n list.forEach(function(f){\n var mine=f.settled_by===\"person\"&&canAnswer;\n var row=el(\"li\",{class:\"asp\"+(mine?\" yours\":\"\")});\n var g=el(\"span\",{class:\"gl\"});g.innerHTML=glyph(\"Open aspect\",DASHED);row.appendChild(g);\n var t=el(\"div\");\n var name=el(\"p\",{class:\"nm\"});name.appendChild(el(\"span\",{class:\"code\"},f.aspect));name.appendChild(el(\"span\",null,\" \"+f.name));t.appendChild(name);\n t.appendChild(el(\"p\",{class:\"ask\"},f.ask));\n if(f.span&&f.span!==r.delivery)t.appendChild(el(\"p\",{class:\"small\"},\"The mission says: \"+f.span));\n if(f.note)t.appendChild(el(\"p\",{class:\"small\"},f.note));\n var who=el(\"div\",{class:\"who\"});\n who.appendChild(el(\"p\",{class:\"small settler\"},(f.settled_by===\"person\"&&!canAnswer?SETTLED.bystander:SETTLED[f.settled_by])||f.settled_by));\n if(mine){\n var go=el(\"button\",{type:\"button\",class:\"iconbtn\",\"aria-label\":\"Go to Instructions\",title:\"Go to Instructions\"});go.innerHTML=ARROW;\n go.addEventListener(\"click\",function(){var box=boxes[r.id];if(box){if(box.scrollIntoView)box.scrollIntoView({block:\"center\"});box.focus();}});\n who.appendChild(go);\n }\n t.appendChild(who);\n var key=r.id+\"/\"+f.aspect;\n var why=el(\"details\");if(whyOpen[key])why.setAttribute(\"open\",\"\");\n why.addEventListener(\"toggle\",function(){whyOpen[key]=why.open;});\n var sum=el(\"summary\");sum.appendChild(el(\"span\",{class:\"closedL\"},\"Why\"));sum.appendChild(el(\"span\",{class:\"openL\"},\"Hide why\"));why.appendChild(sum);\n why.appendChild(el(\"p\",{class:\"small\"},f.why));\n var by=String(f.checked_by||\"\").split(\" \")[0]===\"jev\"?\"Jev\":f.checked_by===\"code\"?\"a check in code\":\"the \"+f.checked_by;\n why.appendChild(el(\"p\",{class:\"small\"},\"Flagged by \"+by+\".\"));\n t.appendChild(why);\n row.appendChild(t);ul.appendChild(row);\n });\n s.appendChild(ul);\n }\n var open={};list.forEach(function(f){open[f.aspect]=true;});\n var unseen=(rv.not_checked||[]).filter(function(n){return !open[n.aspect];});\n var rest=unseen.filter(function(n){return !n.in_part;});\n unseen.filter(function(n){return n.in_part;}).forEach(function(n){\n s.appendChild(stateLine(\"Checked in part\",EYE,function(t){t.appendChild(el(\"span\",null,\"Checked in part: \"));t.appendChild(el(\"span\",{class:\"code\"},n.aspect));t.appendChild(el(\"span\",null,\" \"+n.name+\" (\"+n.in_part+\")\"));}));\n });\n if(rest.length)s.appendChild(stateLine(\"Read it yourself\",EYE,function(t){\n t.appendChild(el(\"span\",null,\"No check covers these, so read the mission for them: \"));\n rest.forEach(function(n,i){if(i)t.appendChild(el(\"span\",null,\" \u00B7 \"));t.appendChild(el(\"span\",{class:\"code\"},n.aspect));t.appendChild(el(\"span\",null,\" \"+n.name));});\n }));\n if(canAnswer&&list.some(function(f){return f.settled_by===\"drafter\";}))s.appendChild(el(\"p\",{class:\"small\"},\"To put the drafting agent's questions to it, say what to revise under Instructions and send it back.\"));\n var refused=r.provider_authorization&&r.provider_authorization.authorized===false;\n if(canAnswer&&list.length&&!refused)s.appendChild(el(\"p\",{class:\"small\"},\"Approving as it stands starts the session, and the worker reads these questions with the task.\"));\n return s;\n }\n function card(r,token){\n var a=el(\"article\",{\"aria-label\":r.title});\n a.appendChild(el(\"span\",{class:\"kind\"},r.kind_label||r.kind));\n a.appendChild(el(\"h2\",null,r.title));\n a.appendChild(el(\"p\",{class:\"meta\"},\"Asked by \"+r.by+(r.via?\" through \"+r.via:\"\")+\" \u00B7 \"+new Date(r.created_at).toLocaleString()+(r.circle?\" \u00B7 \"+r.circle+\" circle\":\"\")));\n a.appendChild(el(\"p\",{class:\"does\"},r.consequence));\n if(r.intention)a.appendChild(el(\"p\",null,r.intention));\n if(!decided[r.id]&&!pending[r.id]&&r.projection&&r.projection.stage===\"awaiting_decision\"&&(r.kind===\"run_request\"||r.kind===\"work_proposal\"||r.draft_review||r.delivery||r.person_words))a.appendChild(standardBlock(r,token));\n var det=el(\"details\");det.appendChild(el(\"summary\",null,\"What exactly is asked\"));det.appendChild(el(\"pre\",null,r.detail||\"\"));a.appendChild(det);\n var status=el(\"p\",{role:\"status\"});\n if(r.projection&&r.projection.stage!==\"awaiting_decision\"&&(!decided[r.id]||[\"recording\",\"dispatching\"].indexOf(r.projection.stage)===-1)){decided[r.id]=projectionView(r.projection);}\n if(decided[r.id]){\n var shown=decided[r.id];a.appendChild(decidedBlock(shown));\n if(shown.stage===\"recording\"&&polls[r.id]===undefined&&host()&&host().callTool){polls[r.id]=0;setTimeout(function(){follow(r);},POLL_MS);}\n if([\"recording\",\"dispatching\"].indexOf(shown.stage)!==-1&&host()&&host().callTool){\n var refresh=el(\"button\",{type:\"button\"},\"Refresh status\");\n refresh.addEventListener(\"click\",function(){\n refresh.disabled=true;\n host().callTool(\"get_run_result\",{record_id:r.id}).then(function(res){\n var out=toolResult(res);var p=out&&(out.projection||(out.data&&out.data.projection));\n if(p){decided[r.id]=projectionView(p);delete errors[r.id];}\n else{errors[r.id]=(out&&out.message)||\"Status could not be refreshed.\";}\n render();\n },function(err){errors[r.id]=\"Status could not be refreshed: \"+String(err&&err.message||err);render();});\n });\n a.appendChild(refresh);\n }\n if(errors[r.id])a.appendChild(el(\"p\",{class:\"fail\",role:\"status\"},errors[r.id]));\n return a;\n }\n function approvalLink(){var p=el(\"p\",{class:\"note\"});p.appendChild(document.createTextNode(\"Decide on its approval page: \"));p.appendChild(el(\"a\",{href:r.approval_url,target:\"_blank\",rel:\"noopener\"},\"open the approval page\"));return p;}\n if(pending[r.id]){status.className=\"note\";status.textContent=\"Recording your decision\u2026\";a.appendChild(status);return a;}\n if(!token||!host()||!host().callTool){a.appendChild(approvalLink());return a;}\n var ta=el(\"textarea\",{\"aria-label\":\"Instructions or changes (needed to approve with changes or send back)\",placeholder:\"Instructions or changes (needed to approve with changes or send back)\"});\n ta.value=typed[r.id]||\"\";boxes[r.id]=ta;\n ta.addEventListener(\"input\",function(){typed[r.id]=ta.value;});\n a.appendChild(ta);\n var row=el(\"div\",{class:\"row\"});\n // The ring on a mission would move with its report and read as a verdict, so a mission's buttons carry none (jgwill/Miadi#939).\n var mission=r.kind===\"run_request\"||r.kind===\"work_proposal\";\n var buttons=[[\"approve\",\"Approve\",mission?\"\":\"primary\"],[\"approve_with_changes\",\"Approve with my changes\",\"\"],[\"request_revision\",\"Send back for revision\",\"\"],[\"decline\",\"Decline\",\"\"]];\n var btns=[];\n buttons.forEach(function(b){var x=el(\"button\",{type:\"button\",class:b[2]},b[1]);x.addEventListener(\"click\",function(){go(b[0]);});btns.push(x);row.appendChild(x);});\n a.appendChild(row);a.appendChild(status);\n if(errors[r.id]){status.className=\"fail\";status.textContent=errors[r.id];if(relink[r.id])a.appendChild(approvalLink());}\n function go(choice){\n var text=ta.value.trim();\n if((choice===\"approve_with_changes\"||choice===\"request_revision\")&&!text){status.className=\"fail\";status.textContent=choice===\"request_revision\"?\"Say what to revise.\":\"Write the changes to apply.\";ta.focus();return;}\n btns.forEach(function(x){x.disabled=true;});status.className=\"note\";status.textContent=\"Recording your decision\u2026\";pending[r.id]=true;delete errors[r.id];\n host().callTool(\"decide_request\",{record_id:r.id,decision:choice,instructions:text||undefined,decision_token:token}).then(function(res){\n var out=toolResult(res);\n // The connector answers with the request's projection, also when a click finds it decided already.\n var proj=out&&(out.projection||(out.detail&&out.detail.projection));\n var code=out&&out.error;\n delete pending[r.id];\n // The token expired or the request was revised, and it still awaits a decision: say so and keep it open.\n if(code&&(!proj||proj.stage===\"awaiting_decision\")){errors[r.id]=(out&&out.message)||\"Not recorded.\";relink[r.id]=true;render();return;}\n var note=out&&out.replayed?\"This decision was already recorded. Nothing ran again.\":code===\"decision_conflict\"?\"A different decision was recorded first. Here is where it stands.\":code===\"already_decided\"?\"This request was already decided. Here is where it stands.\":null;\n if(proj){decided[r.id]=projectionView(proj,note);}\n else{decided[r.id]={cls:code?\"fail\":\"done\",label:code?((out&&out.message)||\"Not recorded.\"):((out&&out.summary)||\"Recorded.\"),lines:[],url:null,note:null};}\n render();\n },function(err){\n // The host gave up waiting; the connector may still have recorded it. Read before offering the buttons again (#924).\n delete pending[r.id];\n decided[r.id]={cls:\"note\",stage:\"recording\",label:\"Checking whether your decision was recorded\u2026\",lines:[],url:null,note:null};\n polls[r.id]=0;render();\n follow(r,\"Not recorded: \"+(err&&err.message?err.message:String(err))+\".\");\n });\n }\n return a;\n }\n window.addEventListener(\"openai:set_globals\",render);\n render();\n})();\n</script></body></html>";
package/dist/decisions.js CHANGED
@@ -234,13 +234,18 @@ summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
234
234
  s.appendChild(ul);
235
235
  }
236
236
  var open={};list.forEach(function(f){open[f.aspect]=true;});
237
- var rest=(rv.not_checked||[]).filter(function(n){return !open[n.aspect];});
237
+ var unseen=(rv.not_checked||[]).filter(function(n){return !open[n.aspect];});
238
+ var rest=unseen.filter(function(n){return !n.in_part;});
239
+ unseen.filter(function(n){return n.in_part;}).forEach(function(n){
240
+ s.appendChild(stateLine("Checked in part",EYE,function(t){t.appendChild(el("span",null,"Checked in part: "));t.appendChild(el("span",{class:"code"},n.aspect));t.appendChild(el("span",null," "+n.name+" ("+n.in_part+")"));}));
241
+ });
238
242
  if(rest.length)s.appendChild(stateLine("Read it yourself",EYE,function(t){
239
243
  t.appendChild(el("span",null,"No check covers these, so read the mission for them: "));
240
244
  rest.forEach(function(n,i){if(i)t.appendChild(el("span",null," · "));t.appendChild(el("span",{class:"code"},n.aspect));t.appendChild(el("span",null," "+n.name));});
241
245
  }));
242
246
  if(canAnswer&&list.some(function(f){return f.settled_by==="drafter";}))s.appendChild(el("p",{class:"small"},"To put the drafting agent's questions to it, say what to revise under Instructions and send it back."));
243
- if(canAnswer&&list.length)s.appendChild(el("p",{class:"small"},"Approving as it stands starts the session, and the worker reads these questions with the task."));
247
+ var refused=r.provider_authorization&&r.provider_authorization.authorized===false;
248
+ if(canAnswer&&list.length&&!refused)s.appendChild(el("p",{class:"small"},"Approving as it stands starts the session, and the worker reads these questions with the task."));
244
249
  return s;
245
250
  }
246
251
  function card(r,token){
@@ -43,6 +43,7 @@ export interface DraftReview {
43
43
  aspect: string;
44
44
  name: string;
45
45
  reason: string;
46
+ in_part?: string;
46
47
  }[];
47
48
  running_missions: RunningMission[];
48
49
  preferences: string[];
@@ -194,7 +194,7 @@ export async function reviewDraft({ record, records, standard, adapter, now = Da
194
194
  notChecked.push({ aspect: a.code, name: a.name, reason: "This version of the connector runs no check for it." });
195
195
  }
196
196
  if (a.not_checked)
197
- notChecked.push({ aspect: a.code, name: a.name, reason: a.not_checked });
197
+ notChecked.push({ aspect: a.code, name: a.name, reason: a.not_checked, ...(a.checked_in_part ? { in_part: a.checked_in_part } : {}) });
198
198
  }
199
199
  // For a finding Jev raised on the mission, the sentence it is about. A failure leaves the finding without one.
200
200
  const raised = needs.flatMap((f) => {
@@ -39,6 +39,8 @@ export interface StandardAspect {
39
39
  jev?: JevCheck;
40
40
  /** Why this version of the connector does not check it, or checks only part of it. */
41
41
  not_checked?: string;
42
+ /** What is checked when only part of the aspect is, in words for the person deciding. */
43
+ checked_in_part?: string;
42
44
  }
43
45
  export interface MissionStandard {
44
46
  name: string;
@@ -57,6 +57,7 @@ export const BUILT_IN_STANDARD = {
57
57
  fix: "Say which in the mission.",
58
58
  settled_by: "drafter",
59
59
  not_checked: "Only shared issues are compared. The local reviewer judges other overlap, and it does not run yet (jgwill/Miadi#939 A3).",
60
+ checked_in_part: "only shared issues are compared",
60
61
  },
61
62
  {
62
63
  code: "S4",
@@ -127,7 +128,7 @@ export function parseStandard(text, version) {
127
128
  if (!["drafter", "person", "local_agent"].includes(a.settled_by))
128
129
  throw new Error(`aspect ${a.code}: settled_by is drafter, person or local_agent`);
129
130
  const jev = checkOf(a.jev);
130
- return { code: a.code, name: a.name, requires: a.requires, why: a.why, ask: a.ask, ...(isText(a.fix) ? { fix: a.fix } : {}), settled_by: a.settled_by, ...(jev ? { jev } : {}), ...(isText(a.not_checked) ? { not_checked: a.not_checked } : {}) };
131
+ return { code: a.code, name: a.name, requires: a.requires, why: a.why, ask: a.ask, ...(isText(a.fix) ? { fix: a.fix } : {}), settled_by: a.settled_by, ...(jev ? { jev } : {}), ...(isText(a.not_checked) ? { not_checked: a.not_checked } : {}), ...(isText(a.checked_in_part) ? { checked_in_part: a.checked_in_part } : {}) };
131
132
  });
132
133
  const preferences = {};
133
134
  for (const [person, list] of Object.entries(raw.preferences ?? {})) {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.22";
1
+ export declare const VERSION = "0.1.23";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.1.22";
1
+ export const VERSION = "0.1.23";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miadi/connector",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "The Miadi connector: one remote MCP service through which a person signed in with their Miadi person token, from ChatGPT or any MCP client, reads sessions, reviews, episodes and circles and records observations, inquiries, handoffs and proposals with their name on each record.",
5
5
  "license": "MIT",
6
6
  "author": "Miadi",