@miadi/connector 0.1.12 → 0.1.13

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/tools.js CHANGED
@@ -18,8 +18,9 @@ import { CIRCLES, CIRCLE_IDS, CLAIM_CATEGORIES, DOMAINS, HANDOFF_STATES, current
18
18
  import { VERSION } from "./version.js";
19
19
  import { AlreadyDecided, DECISIONS, KIND_LABELS, consequenceOf, decide, isApprovable, readQueueState, requestProjection } from "./approve.js";
20
20
  import { DECISIONS_CARD_HTML, DECISIONS_CARD_URI, DECISION_TOKENS_META } from "./decisions.js";
21
- import { discoverProviders, resolveProvider, routerPreflight, PROVIDERS } from "./providers.js";
22
- import { missionStatus } from "./mission-status.js";
21
+ import { discoverProviders, jevConfigOf, resolveProvider, routerPreflight, PROVIDERS } from "./providers.js";
22
+ import { authorizesActions, missionStatus, primaryIssueOf, resolveMissionSubject } from "./mission-status.js";
23
+ import { candidatesFor, CRITICALITIES, jevAdapter, modelPreflight, PREFLIGHT_CONTRACT, PREFLIGHT_VERSION, TASK_TYPES, } from "./model-preflight.js";
23
24
  import { checkGrant, isBusy, steerAttempts, steeringGrantOf, steerSession } from "./steer.js";
24
25
  import { join } from "node:path";
25
26
  import { artefactVisibility, readArtefactWeave, saveResearchArtefact } from "@miadi/inquiry-weave";
@@ -84,6 +85,29 @@ const WRITE = { readOnlyHint: false, destructiveHint: false, openWorldHint: fals
84
85
  const refs = z.array(z.string()).max(50).default([]).describe("References: miadi-review:<uuid>, miadi-chronicle://<n>, owner/repo#n, session:<uuid>, a record id, or a URL.");
85
86
  const circle = z.enum(CIRCLE_IDS);
86
87
  const claim = z.enum(CLAIM_CATEGORIES).describe("observed, hypothesis, desired_future, fiction or verified_implementation.");
88
+ const primaryIssue = z
89
+ .string()
90
+ .regex(/^[\w.-]+\/[\w.-]+#\d+$/)
91
+ .optional()
92
+ .describe("owner/repo#n: the one issue this mission implements. Other refs stay related. Without it, mission_status can only infer the mission from the first issue in refs (jgwill/Miadi#913).");
93
+ /** Refs with the declared primary issue first, never duplicated. */
94
+ function withPrimary(refs, primary) {
95
+ return primary ? [primary, ...refs.filter((r) => r !== primary)] : refs;
96
+ }
97
+ /**
98
+ * Why GitHub did not answer a reference (jgwill/Miadi#913 B). A 404 without a
99
+ * token is a private repository as often as a missing issue, so nothing here is
100
+ * ever reported as not found.
101
+ */
102
+ export function githubAccess(status, hasToken) {
103
+ if (status === 404)
104
+ return hasToken ? "not_visible_to_token" : "no_github_token";
105
+ if (status === 401)
106
+ return "token_rejected";
107
+ if (status === 403 || status === 429)
108
+ return "forbidden_or_rate_limited";
109
+ return `github_${status}`;
110
+ }
87
111
  function episodeRef(input) {
88
112
  // miadi-chronicle://316, miadi-chronicle:316, chronicle:<folder>, a folder, or a number
89
113
  return encodeURIComponent(input.replace(/^miadi-chronicle:(\/\/)?/, "").replace(/^chronicle:/, "").trim());
@@ -227,7 +251,7 @@ export function registerTools(server, ctx) {
227
251
  signal: AbortSignal.timeout(15_000),
228
252
  });
229
253
  if (!res.ok)
230
- return { ref: r, kind: "github_issue", state: res.status === 404 ? "not_found_or_private" : `github_${res.status}`, cited_by: cited };
254
+ return { ref: r, kind: "github_issue", state: "unverified", access: githubAccess(res.status, Boolean(process.env.GITHUB_TOKEN)), cited_by: cited };
231
255
  const i = await res.json();
232
256
  return {
233
257
  ref: r,
@@ -625,7 +649,7 @@ export function registerTools(server, ctx) {
625
649
  }));
626
650
  server.registerTool("mission_status", {
627
651
  title: "Where a mission stands",
628
- description: "One mission, joined and verified: its source request and decision, the sessions bound to it (live or not), its issues and PRs, what is approved, queued, delivered, running, merged, released or closed — each by its evidence, never inferred from an idle pane — and the next authorized action. Operator details need Miadi's write tier. jgwill/Miadi#893.",
652
+ description: "One mission, joined and verified: its source request and decision, the sessions bound to it (live or not), its issues and PRs, what is approved, queued, delivered, running, merged, released or closed — each by its evidence, never inferred from an idle pane — and the next authorized action. An issue or session resolves only through the request that holds it as primary; several are ambiguous_mission, citations alone are related_only, and an inference authorizes nothing (identity.authorizes_actions). Released needs a release receipt with artifact, target, production check and a fresh time; closed needs an approved session ending that cites the mission. Operator details need Miadi's write tier. jgwill/Miadi#893, #913.",
629
653
  inputSchema: {
630
654
  ref: z
631
655
  .string()
@@ -667,10 +691,11 @@ export function registerTools(server, ctx) {
667
691
  }
668
692
  const res = await f(`https://api.github.com/repos/${owner}/${repo}/issues/${n}`, { headers, signal: AbortSignal.timeout(15_000) });
669
693
  if (!res.ok)
670
- return { state: res.status === 404 ? "not_found_or_private" : `github_${res.status}` };
694
+ return { state: "unverified", access: githubAccess(res.status, Boolean(process.env.GITHUB_TOKEN)) };
671
695
  const i = await res.json();
672
696
  return { state: i.state, title: i.title };
673
697
  },
698
+ liveVersionOf: (pkg) => (pkg === "@miadi/connector" ? VERSION : undefined),
674
699
  });
675
700
  }));
676
701
  server.registerTool("provider_capabilities", {
@@ -689,6 +714,63 @@ export function registerTools(server, ctx) {
689
714
  note: "A provider name in a task's text selects nothing; request_authorized_run's provider field is verified against this list, and an unavailable provider leaves the mission unstarted.",
690
715
  };
691
716
  }));
717
+ server.registerTool("mission_model_preflight", {
718
+ title: "Which provider and model fits a mission, and how that is known",
719
+ description: "A typed, versioned, read-only preflight for one mission (jgwill/Miadi#913): the candidate providers with their concrete model when the host shows one, sign-in evidence (config directory only), quota unknown, policy eligibility (the approved provider is kept; Claude Code only when named), a review floor, and a recommendation or an abstention with its reasons. It says where the choice came from: static_fallback, jev or a synthetic fixture. Jev is asked only with consult_jev and a Jev key in the connector's environment; that call is charged, and it carries no task text, mission id or repository name. A reported launch on another provider than the approved one fails closed as provider_mismatch. It authorizes nothing. Needs Miadi's write tier.",
720
+ inputSchema: {
721
+ ref: z.string().min(1).describe("The mission: run_request:<id> or work_proposal:<id> (an issue or session ref resolves only through its primary relation)."),
722
+ task_type: z.enum(TASK_TYPES),
723
+ criticality: z.enum(CRITICALITIES).default("normal"),
724
+ consult_jev: z.boolean().default(false).describe("Ask Jev to choose among the eligible options. Charged by Jev; off by default."),
725
+ launch: z
726
+ .object({
727
+ provider: z.enum(["codex", "pi", "copilot", "claude"]),
728
+ model: z.string().max(120).optional(),
729
+ evidence: z.string().min(1).max(500).describe("What shows it: a process line, a pane header."),
730
+ })
731
+ .optional()
732
+ .describe("What actually launched, as observed. A provider other than the approved one fails closed."),
733
+ },
734
+ annotations: READ,
735
+ }, wrap(async (a, extra) => {
736
+ const { person } = who(extra);
737
+ requireTier(person, "write");
738
+ const found = resolveMissionSubject(String(a.ref).trim(), records());
739
+ if (!found?.record) {
740
+ return {
741
+ contract: PREFLIGHT_CONTRACT,
742
+ version: PREFLIGHT_VERSION,
743
+ state: "mission_unresolved",
744
+ ref: a.ref,
745
+ identity: found ? { resolution: found.resolution, candidates: found.candidates } : { resolution: "not_found", candidates: [] },
746
+ note: "Name one mission by its record id; no model is chosen for an unidentified mission.",
747
+ };
748
+ }
749
+ const record = found.record;
750
+ const body = record.body;
751
+ const provider = (typeof body.resolved_provider === "string" ? body.resolved_provider : typeof body.provider === "string" ? body.provider : null);
752
+ const basis = record.approval_state === "approved" ? "approved" : "requested";
753
+ const candidates = candidatesFor(discoverProviders(ctx.providersProbe), provider, ctx.modelOf, basis);
754
+ const jevConfig = jevConfigOf(process.env);
755
+ const jev = a.consult_jev ? (ctx.jevAdapter ?? (jevConfig ? jevAdapter(jevConfig, ctx.approval?.fetchImpl ?? fetch) : undefined)) : undefined;
756
+ const configured = Boolean(jevConfig);
757
+ const primary = primaryIssueOf(record);
758
+ return modelPreflight({
759
+ mission: {
760
+ id: record.id,
761
+ resolution: found.resolution,
762
+ authorizes_actions: authorizesActions(found),
763
+ ...(primary ? { primary_issue: primary.ref } : {}),
764
+ },
765
+ approved_provider: provider,
766
+ provider_basis: basis,
767
+ task: a.task_type,
768
+ criticality: a.criticality,
769
+ candidates,
770
+ ...(a.launch ? { launch: a.launch } : {}),
771
+ ...(jev ? { adapter: jev } : { routerWhenAbsent: configured ? "not_requested" : "router_unconfigured" }),
772
+ });
773
+ }));
692
774
  // search / fetch: the pair ChatGPT uses for connectors in search and deep research.
693
775
  server.registerTool("search", {
694
776
  title: "Search Miadi",
@@ -975,6 +1057,7 @@ export function registerTools(server, ctx) {
975
1057
  desired_outcome: z.string().min(1).max(6000),
976
1058
  circle,
977
1059
  refs,
1060
+ primary_issue: primaryIssue,
978
1061
  repo: z.string().max(120).optional(),
979
1062
  ...base,
980
1063
  },
@@ -984,13 +1067,13 @@ export function registerTools(server, ctx) {
984
1067
  requireContribute(auth);
985
1068
  const r = append("work_proposal", person, auth, {
986
1069
  circle: a.circle,
987
- refs: a.refs,
1070
+ refs: withPrimary(a.refs, a.primary_issue),
988
1071
  intention: a.intention,
989
1072
  role: a.role,
990
1073
  idempotency_key: a.idempotency_key,
991
1074
  state: "pending",
992
1075
  approval_state: "awaiting_approval",
993
- body: { title: a.title, desired_outcome: a.desired_outcome, repo: a.repo },
1076
+ body: { title: a.title, desired_outcome: a.desired_outcome, repo: a.repo, ...(a.primary_issue ? { primary_issue: a.primary_issue } : {}) },
994
1077
  });
995
1078
  return { ...r, approval_url: approvalUrl(r.id) };
996
1079
  }));
@@ -1027,6 +1110,11 @@ export function registerTools(server, ctx) {
1027
1110
  session_id: z.string().min(8),
1028
1111
  ending: z.enum(["exit", "complete_then_exit", "record_only"]),
1029
1112
  reason: z.string().min(1).max(4000),
1113
+ mission: z
1114
+ .string()
1115
+ .regex(/^(run_request|work_proposal):[\w-]+$/)
1116
+ .optional()
1117
+ .describe("The mission this ending closes, by record id. Only an approved exit or complete_then_exit that names the mission here closes it in mission_status (jgwill/Miadi#913)."),
1030
1118
  evidence_refs: refs,
1031
1119
  },
1032
1120
  annotations: WRITE,
@@ -1036,11 +1124,11 @@ export function registerTools(server, ctx) {
1036
1124
  const id = a.session_id.replace(/^session:/, "");
1037
1125
  const r = append("session_completion_proposal", person, auth, {
1038
1126
  circle: "continuity",
1039
- refs: [`session:${id}`],
1127
+ refs: a.mission ? [`session:${id}`, a.mission] : [`session:${id}`],
1040
1128
  evidence_refs: a.evidence_refs,
1041
1129
  approval_state: "awaiting_approval",
1042
- idempotency_key: `${id}:${a.ending}`,
1043
- body: { session_id: id, ending: a.ending, reason: a.reason },
1130
+ idempotency_key: a.mission ? `${id}:${a.ending}:${a.mission}` : `${id}:${a.ending}`,
1131
+ body: { session_id: id, ending: a.ending, reason: a.reason, ...(a.mission ? { mission: a.mission } : {}) },
1044
1132
  });
1045
1133
  return { ...r, approval_url: approvalUrl(r.id) };
1046
1134
  }));
@@ -1052,6 +1140,7 @@ export function registerTools(server, ctx) {
1052
1140
  task: z.string().min(1).max(12000).describe("What the session does and what it returns."),
1053
1141
  circle,
1054
1142
  refs,
1143
+ primary_issue: primaryIssue,
1055
1144
  session: z.string().regex(/^[A-Za-z0-9._-]{3,80}$/).optional().describe("An existing tmux session to hand it to, or a name for a new one."),
1056
1145
  cwd: z.string().max(300).optional().describe("Working folder for a new session (default the Miadi checkout)."),
1057
1146
  provider: z
@@ -1081,7 +1170,7 @@ export function registerTools(server, ctx) {
1081
1170
  }
1082
1171
  const r = append("run_request", person, auth, {
1083
1172
  circle: a.circle,
1084
- refs: a.refs,
1173
+ refs: withPrimary(a.refs, a.primary_issue),
1085
1174
  intention: a.intention,
1086
1175
  role: a.role,
1087
1176
  idempotency_key: a.idempotency_key,
@@ -1093,6 +1182,7 @@ export function registerTools(server, ctx) {
1093
1182
  cwd: a.cwd,
1094
1183
  provider: a.provider,
1095
1184
  resolved_provider: resolution.provider.id,
1185
+ ...(a.primary_issue ? { primary_issue: a.primary_issue } : {}),
1096
1186
  ...(a.steering ? { steering: a.steering } : {}),
1097
1187
  },
1098
1188
  });
@@ -1613,6 +1703,7 @@ export const TOOL_STATUS = {
1613
1703
  list_records: "live",
1614
1704
  get_run_result: "live",
1615
1705
  mission_status: "live (operator details need the Miadi write tier)",
1706
+ mission_model_preflight: "live, Miadi write tier (static policy; Jev only with consult_jev and a Jev key on the host, otherwise router_unconfigured)",
1616
1707
  provider_capabilities: "live, Miadi write tier",
1617
1708
  search: "live",
1618
1709
  fetch: "live",
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.12";
1
+ export declare const VERSION = "0.1.13";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.1.12";
1
+ export const VERSION = "0.1.13";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miadi/connector",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
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",