@miadi/connector 0.1.9 → 0.1.11

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,9 +18,13 @@ 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";
23
+ import { checkGrant, isBusy, steerAttempts, steeringGrantOf, steerSession } from "./steer.js";
21
24
  import { join } from "node:path";
22
25
  import { artefactVisibility, readArtefactWeave, saveResearchArtefact } from "@miadi/inquiry-weave";
23
26
  import { ResearchUploads, UploadRefusal } from "./uploads.js";
27
+ import { DEFAULT_PAGE_BYTES, FileRefusal, MAX_TEXT_BYTES, MIN_PAGE_BYTES, TEXT_TYPES, checkRelativePath, episodeNumberOf, extensionOf, fileRef, pageOf, parseFileRef, } from "./episode-files.js";
24
28
  class ToolRefusal extends Error {
25
29
  code;
26
30
  detail;
@@ -60,6 +64,8 @@ function wrap(fn) {
60
64
  return fail({ error: error.code, message: error.message, ...(error.detail ? { detail: error.detail } : {}) });
61
65
  if (error instanceof UploadRefusal)
62
66
  return fail({ error: "invalid_upload", message: error.message });
67
+ if (error instanceof FileRefusal)
68
+ return fail({ error: error.code, message: error.message });
63
69
  if (error instanceof MiadiError) {
64
70
  const unavailable = error.status === 0;
65
71
  return fail({
@@ -324,12 +330,105 @@ export function registerTools(server, ctx) {
324
330
  ]);
325
331
  return {
326
332
  episode: decodeURIComponent(ref),
327
- files,
333
+ files: withReadRefs(files),
328
334
  screenwalks,
329
335
  reviews: epReviews,
330
336
  connector_records: touching(`miadi-chronicle://${decodeURIComponent(ref)}`),
331
337
  };
332
338
  }));
339
+ /**
340
+ * One text file of one episode, read as the person through Miadi's file
341
+ * door; Miadi applies their audience, the room rule and the vessel's path
342
+ * guard, and the path is checked here before a URL is built (jgwill/Miadi#815).
343
+ */
344
+ const readEpisodeFile = async (person, a) => {
345
+ const path = a.path.trim();
346
+ const segments = checkRelativePath(path);
347
+ const extension = extensionOf(path);
348
+ const contentType = TEXT_TYPES[extension];
349
+ if (!contentType) {
350
+ throw new FileRefusal("unsupported", `${path} is not read as text here: only ${Object.keys(TEXT_TYPES).join(", ")} files are. Miadi serves documents (html, pdf, csv, svg) as downloads and images and media by their own routes; get_evidence lists them.`);
351
+ }
352
+ const ref = episodeRef(a.episode);
353
+ // A number, epNNN or a folder name; `..` would be folded by the URL parser into another route.
354
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(decodeURIComponent(ref))) {
355
+ throw new FileRefusal("invalid_path", "Name the episode as miadi-chronicle://<n>, its number or its folder.");
356
+ }
357
+ const url = `/api/chronicle/episodes/${ref}/files/${segments.map(encodeURIComponent).join("/")}`;
358
+ let text;
359
+ try {
360
+ text = await miadi.get(person.token, url);
361
+ }
362
+ catch (error) {
363
+ if (!(error instanceof MiadiError))
364
+ throw error;
365
+ const body = (error.body ?? {});
366
+ const detail = { episode: decodeURIComponent(ref), path, miadi: { status: error.status, ...body } };
367
+ if (error.status === 0)
368
+ throw new ToolRefusal("unavailable", "Miadi could not be reached.", detail);
369
+ if (body.error === "not_in_the_room")
370
+ throw new ToolRefusal("forbidden", body.message ?? "This file is not part of the episode room.", detail);
371
+ if (error.status === 401 || error.status === 403)
372
+ throw new ToolRefusal("forbidden", body.message ?? "Miadi does not let this person read that file.", detail);
373
+ if (error.status === 404)
374
+ throw new ToolRefusal("not_found", body.message ?? `No file ${path} in ${decodeURIComponent(ref)}.`, detail);
375
+ if (body.error === "TOO_LARGE")
376
+ throw new ToolRefusal("too_large", `${path} is larger than the ${MAX_TEXT_BYTES / 1024} KiB Miadi reads as text whole; nothing of it is returned.`, detail);
377
+ if (error.status === 415)
378
+ throw new ToolRefusal("unsupported", body.message ?? `${path} is not text Miadi reads.`, detail);
379
+ if (error.status === 400)
380
+ throw new ToolRefusal("invalid_path", body.message ?? `${path} is not a readable path in the episode.`, detail);
381
+ throw error;
382
+ }
383
+ if (typeof text?.content !== "string")
384
+ throw new ToolRefusal("unsupported", `Miadi did not return ${path} as text.`);
385
+ const number = episodeNumberOf(text.episode);
386
+ const episodeAddress = number === null ? `chronicle:${text.episode}` : `miadi-chronicle://${number}`;
387
+ return {
388
+ ref: fileRef(number ?? text.episode, text.file ?? path),
389
+ episode: { ref: episodeAddress, number, folder: text.episode },
390
+ path: text.file ?? path,
391
+ content_type: contentType,
392
+ ...pageOf(text, { offset: a.offset, maxBytes: a.max_bytes, revision: a.revision }),
393
+ };
394
+ };
395
+ /** Each text file Miadi lists as previewable carries the ref read_episode_file and fetch take. */
396
+ const withReadRefs = (listing) => {
397
+ const folder = listing?.episode?.path;
398
+ if (!folder || !Array.isArray(listing?.files))
399
+ return listing;
400
+ const number = listing.episode.number ?? episodeNumberOf(folder);
401
+ return {
402
+ ...listing,
403
+ files: listing.files.map((f) => f?.kind === "text" && f.previewable && TEXT_TYPES[extensionOf(String(f.relativePath))]
404
+ ? { ...f, read_ref: fileRef(number ?? folder, f.relativePath) }
405
+ : f),
406
+ connector_read: "A file with read_ref is read whole, in pages if long, by read_episode_file or fetch. Files without one are listed only.",
407
+ };
408
+ };
409
+ server.registerTool("read_episode_file", {
410
+ title: "Read one file an episode holds",
411
+ description: "The full UTF-8 text of one text file (md, txt, yaml, yml, json) in a Chronicle episode, read as you through Miadi, with its content type, byte count and sha256. Name the episode and the path get_evidence lists (or pass its read_ref as episode alone). A file longer than max_bytes comes in pages: pass page.next back to read the next one; joined, the pages hash to sha256. Read only: nothing is changed.",
412
+ inputSchema: {
413
+ episode: z.string().min(1).describe("miadi-chronicle://<n>, an episode number or its folder; or a read_ref (miadi-chronicle://<n>/<path>) with path left out."),
414
+ path: z.string().optional().describe("The file's path inside the episode, as get_evidence lists it, e.g. foundations/broker-adapters/README.md."),
415
+ offset: z.number().int().min(0).optional().describe("0, or page.next.offset from the previous page."),
416
+ revision: z.string().regex(/^[0-9a-f]{64}$/).optional().describe("page.next.revision from the previous page: refuses with changed if the file moved since."),
417
+ max_bytes: z.number().int().min(MIN_PAGE_BYTES).max(MAX_TEXT_BYTES).optional().describe(`Page size in bytes (default ${DEFAULT_PAGE_BYTES}).`),
418
+ },
419
+ annotations: READ,
420
+ }, wrap(async (a, extra) => {
421
+ const { person } = who(extra);
422
+ let episode = a.episode;
423
+ let path = a.path;
424
+ if (!path) {
425
+ const parsed = parseFileRef(a.episode);
426
+ if (!parsed)
427
+ throw new FileRefusal("invalid_path", "Name the file: give path, or pass a read_ref (miadi-chronicle://<n>/<path>) as episode.");
428
+ ({ episode, path } = parsed);
429
+ }
430
+ return readEpisodeFile(person, { ...a, episode, path });
431
+ }));
333
432
  server.registerTool("get_evaluations", {
334
433
  title: "Evaluations: provider traces and recorded assessments",
335
434
  description: "The tracing records (LLMOps provider traces and prompts) an episode holds, and the assessments people recorded with record_evaluation, operational and creator-experience kept apart.",
@@ -468,6 +567,72 @@ export function registerTools(server, ctx) {
468
567
  : {}),
469
568
  };
470
569
  }));
570
+ server.registerTool("mission_status", {
571
+ title: "Where a mission stands",
572
+ 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.",
573
+ inputSchema: {
574
+ ref: z
575
+ .string()
576
+ .min(1)
577
+ .describe("run_request:<id>, work_proposal:<id>, another record id, session:<uuid>, or owner/repo#n the mission cites."),
578
+ },
579
+ annotations: READ,
580
+ }, wrap(async ({ ref }, extra) => {
581
+ const { person } = who(extra);
582
+ const writeTier = person.api_tier === "write";
583
+ return missionStatus(ref.trim(), records(), {
584
+ projectOf: (r) => requestProjection(r, { reviewBase: ctx.approval?.config.reviewUrl, factoryDir: ctx.factoryDir }),
585
+ queueOf: (r) => (r.kind === "run_request" || r.kind === "work_proposal" ? readQueueState(r, ctx.factoryDir) : undefined),
586
+ tmuxSessions: writeTier ? (ctx.tmuxSessions ?? (() => [])) : undefined,
587
+ paneBusy: writeTier && ctx.tmux
588
+ ? (session) => {
589
+ try {
590
+ const pane = ctx.tmux.listPanes(session)[0];
591
+ if (!pane)
592
+ return "unknown";
593
+ return isBusy(ctx.tmux.capture(`${session}:${pane.window}.${pane.pane}`)) ? "busy" : "idle";
594
+ }
595
+ catch {
596
+ return "unknown";
597
+ }
598
+ }
599
+ : undefined,
600
+ sessionOf: writeTier ? (id) => miadi.get(person.token, `/api/session/${encodeURIComponent(id)}`) : undefined,
601
+ issueOf: async (owner, repo, n) => {
602
+ const f = ctx.approval?.fetchImpl ?? fetch;
603
+ const headers = {
604
+ accept: "application/vnd.github+json",
605
+ ...(process.env.GITHUB_TOKEN ? { authorization: "Bearer " + process.env.GITHUB_TOKEN } : {}),
606
+ };
607
+ const pr = await f(`https://api.github.com/repos/${owner}/${repo}/pulls/${n}`, { headers, signal: AbortSignal.timeout(15_000) });
608
+ if (pr.ok) {
609
+ const p = await pr.json();
610
+ return { state: p.state, title: p.title, merged: Boolean(p.merged_at) };
611
+ }
612
+ const res = await f(`https://api.github.com/repos/${owner}/${repo}/issues/${n}`, { headers, signal: AbortSignal.timeout(15_000) });
613
+ if (!res.ok)
614
+ return { state: res.status === 404 ? "not_found_or_private" : `github_${res.status}` };
615
+ const i = await res.json();
616
+ return { state: i.state, title: i.title };
617
+ },
618
+ });
619
+ }));
620
+ server.registerTool("provider_capabilities", {
621
+ title: "Which agent providers this host can launch",
622
+ description: "The provider reality of the connector host (jgwill/Miadi#893): which agent CLIs are installed, their versions, whether sign-in evidence exists, the preference order (Codex, Pi, Copilot; Claude Code only on explicit request), and the read-only Jev routing preflight. No token is read or shown. Needs Miadi's write tier: this is host truth.",
623
+ inputSchema: {},
624
+ annotations: READ,
625
+ }, wrap(async (_args, extra) => {
626
+ const { person } = who(extra);
627
+ requireTier(person, "write");
628
+ return {
629
+ preference_order: PROVIDERS.map((p) => p.id),
630
+ explicit_only: PROVIDERS.filter((p) => !p.selectable_by_default).map((p) => p.id),
631
+ providers: discoverProviders(ctx.providersProbe),
632
+ router: await routerPreflight(process.env, ctx.approval?.fetchImpl ?? fetch),
633
+ 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.",
634
+ };
635
+ }));
471
636
  // search / fetch: the pair ChatGPT uses for connectors in search and deep research.
472
637
  server.registerTool("search", {
473
638
  title: "Search Miadi",
@@ -494,11 +659,29 @@ export function registerTools(server, ctx) {
494
659
  }));
495
660
  server.registerTool("fetch", {
496
661
  title: "Fetch one item found by search",
497
- description: "The full text of an item returned by search: a review (miadi-review:<uuid>) or a connector record id.",
662
+ description: "The full text of an item returned by search: a review (miadi-review:<uuid>) or a connector record id; or of an episode file by the read_ref get_evidence lists (miadi-chronicle://<n>/<path>), its first page when long.",
498
663
  inputSchema: { id: z.string().min(1) },
499
664
  annotations: READ,
500
665
  }, wrap(async ({ id }, extra) => {
501
- who(extra);
666
+ const { person } = who(extra);
667
+ const file = parseFileRef(id);
668
+ if (file) {
669
+ const read = await readEpisodeFile(person, file);
670
+ return {
671
+ id: read.ref,
672
+ title: `${read.path} (${read.episode.ref})`,
673
+ text: read.text,
674
+ url: `${config.miadiPublicUrl}/chronicle/${encodeURIComponent(read.episode.folder)}`,
675
+ metadata: {
676
+ content_type: read.content_type,
677
+ bytes: read.bytes,
678
+ sha256: read.sha256,
679
+ verified: read.verified,
680
+ complete: read.page.complete,
681
+ ...(read.page.next ? { next: { tool: "read_episode_file", episode: read.ref, ...read.page.next } } : {}),
682
+ },
683
+ };
684
+ }
502
685
  const reviewId = reviewIdOf(id);
503
686
  if (reviewId) {
504
687
  const r = await reviews.get(reviewId);
@@ -815,12 +998,31 @@ export function registerTools(server, ctx) {
815
998
  refs,
816
999
  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."),
817
1000
  cwd: z.string().max(300).optional().describe("Working folder for a new session (default the Miadi checkout)."),
1001
+ provider: z
1002
+ .enum(["codex", "pi", "copilot", "claude"])
1003
+ .optional()
1004
+ .describe("The provider the run should launch with, verified installed on the connector host. Claude Code runs only when named explicitly. Unknown or unavailable leaves the mission unstarted."),
1005
+ steering: z
1006
+ .object({
1007
+ max_attempts: z.number().int().min(1).max(20).describe("How many authorized follow-up steers this approval covers."),
1008
+ expires_at: z.string().optional().describe("ISO time after which no steer runs under this grant."),
1009
+ pane: z.string().regex(/^\d+\.\d+$/).optional().describe("The pane the seat lives in (window.pane); default the session's first pane."),
1010
+ })
1011
+ .optional()
1012
+ .describe("The steering grant the approver covers with this run: bounded follow-ups to the same session through request_session_steer."),
818
1013
  ...base,
819
1014
  },
820
1015
  annotations: WRITE,
821
1016
  }, wrap(async (a, extra) => {
822
1017
  const { person, auth } = who(extra);
823
1018
  requireContribute(auth);
1019
+ const found = discoverProviders(ctx.providersProbe);
1020
+ const resolution = resolveProvider(a.provider, found);
1021
+ if (resolution.state === "provider_unknown")
1022
+ throw new ToolRefusal("invalid_request", resolution.message, { available: resolution.available });
1023
+ if (resolution.state !== "ok") {
1024
+ return { state: resolution.state, message: resolution.message, available: resolution.available, note: "Nothing was recorded or queued; the mission is unstarted." };
1025
+ }
824
1026
  const r = append("run_request", person, auth, {
825
1027
  circle: a.circle,
826
1028
  refs: a.refs,
@@ -829,9 +1031,100 @@ export function registerTools(server, ctx) {
829
1031
  idempotency_key: a.idempotency_key,
830
1032
  approval_state: "awaiting_approval",
831
1033
  state: "pending",
832
- body: { task: a.task, session: a.session, cwd: a.cwd },
1034
+ body: {
1035
+ task: a.task,
1036
+ session: a.session,
1037
+ cwd: a.cwd,
1038
+ provider: a.provider,
1039
+ resolved_provider: resolution.provider.id,
1040
+ ...(a.steering ? { steering: a.steering } : {}),
1041
+ },
833
1042
  });
834
- return { ...r, approval_url: approvalUrl(r.id) };
1043
+ return {
1044
+ ...r,
1045
+ provider: { requested: a.provider ?? null, resolved: resolution.provider.id, explicit: resolution.explicit, version: resolution.provider.version },
1046
+ approval_url: approvalUrl(r.id),
1047
+ };
1048
+ }));
1049
+ server.registerTool("request_session_steer", {
1050
+ title: "Send one authorized follow-up to a running agent",
1051
+ description: "Steer one existing live agent session within an approved run's steering grant (jgwill/Miadi#893): the exact instruction is typed into the mission's bound pane — never whichever pane is active — after a peek, and a re-peek verifies the agent accepted it. When the approved run carries no steering grant covering this, the steer waits for one explicit scoped human decision on the returned approval_url. Every attempt leaves a durable artifact with its hash and both captures.",
1052
+ inputSchema: {
1053
+ run_request: z.string().min(1).describe("The approved run_request or work_proposal whose steering grant covers this steer."),
1054
+ instruction: z.string().min(1).max(4000).describe("The exact instruction typed into the session. It is material within the approved mission's scope, not new authority."),
1055
+ session: z.string().regex(/^[A-Za-z0-9._-]{3,80}$/).optional().describe("The session to steer; must be the one the grant binds."),
1056
+ pane: z.string().regex(/^\d+\.\d+$/).optional().describe("The pane (window.pane); default the grant's pane, else the session's first pane. The active pane is never used."),
1057
+ refs,
1058
+ ...base,
1059
+ },
1060
+ annotations: WRITE,
1061
+ }, wrap(async (a, extra) => {
1062
+ const { person, auth } = who(extra);
1063
+ requireContribute(auth);
1064
+ requireTier(person, "write");
1065
+ if (!ctx.tmux)
1066
+ throw new ToolRefusal("unavailable", "The connector host has no steering surface; the steer is unstarted.");
1067
+ const grantRecord = all().find((r) => r.id === a.run_request);
1068
+ if (!grantRecord || (grantRecord.kind !== "run_request" && grantRecord.kind !== "work_proposal")) {
1069
+ throw new ToolRefusal("invalid_ref", "Give the id of the approved run_request or work_proposal this steer belongs to.");
1070
+ }
1071
+ if (grantRecord.approval_state !== "approved") {
1072
+ throw new ToolRefusal("not_approved", `${grantRecord.id} is ${grantRecord.approval_state ?? "not approved"}; a steer needs an approved run's grant or its own human decision.`);
1073
+ }
1074
+ const grant = steeringGrantOf(grantRecord);
1075
+ const priorAttempts = steerAttempts(all(), grantRecord.id);
1076
+ const covered = checkGrant(grant, priorAttempts) === "ok";
1077
+ const body = {
1078
+ instruction: a.instruction,
1079
+ session: a.session ?? grantRecord.body.session,
1080
+ pane: a.pane ?? grant?.pane,
1081
+ };
1082
+ const fields = {
1083
+ circle: grantRecord.circle,
1084
+ refs: [grantRecord.id, ...a.refs],
1085
+ intention: a.intention,
1086
+ role: a.role,
1087
+ idempotency_key: a.idempotency_key,
1088
+ body,
1089
+ };
1090
+ if (!covered) {
1091
+ // One explicit scoped human decision (jgwill/Miadi#893): the record waits
1092
+ // on the approval page; approving it runs exactly this instruction once.
1093
+ const r = append("session_steer_request", person, auth, { ...fields, approval_state: "awaiting_approval", state: "pending" });
1094
+ return {
1095
+ ...r,
1096
+ approval_url: approvalUrl(r.id),
1097
+ state: "awaiting_approval",
1098
+ reason: !grant ? "no_grant" : checkGrant(grant, priorAttempts),
1099
+ message: "The approved run's steering grant does not cover this steer; a person's decision is requested, once, for exactly this instruction.",
1100
+ };
1101
+ }
1102
+ const r = append("session_steer_request", person, auth, { ...fields, approval_state: "not_required" });
1103
+ const result = await steerSession({ tmux: ctx.tmux, stateDir: config.stateDir, ...(ctx.steerSettleMs !== undefined ? { settleMs: ctx.steerSettleMs } : {}) }, { record: r, grantRecord, priorAttempts });
1104
+ const delivered = result.status === "accepted" || result.status === "submitted";
1105
+ ledger.append({
1106
+ id: newRecordId(r.kind),
1107
+ kind: r.kind,
1108
+ created_at: new Date().toISOString(),
1109
+ actor: r.actor,
1110
+ refs: r.refs,
1111
+ evidence_refs: [],
1112
+ supersedes: r.id,
1113
+ approval_state: "not_required",
1114
+ state: delivered ? "completed" : "blocked",
1115
+ completion_receipt: {
1116
+ decision: "covered_by_grant",
1117
+ decided_by: { person_id: person.id, name: person.name },
1118
+ decided_at: new Date().toISOString(),
1119
+ outcome: delivered ? result.status : "failed",
1120
+ steer_status: result.status,
1121
+ message: result.message,
1122
+ ...(result.target ? { target: result.target } : {}),
1123
+ ...(result.artifact ? { artifact: result.artifact } : {}),
1124
+ },
1125
+ body: r.body,
1126
+ });
1127
+ return records().find((x) => x.id === r.id) ?? r;
835
1128
  }));
836
1129
  server.registerTool("request_review_operation", {
837
1130
  title: "Request a review operation",
@@ -1255,6 +1548,7 @@ export const TOOL_STATUS = {
1255
1548
  list_sessions: "live, Miadi write tier",
1256
1549
  get_session: "live, Miadi write tier",
1257
1550
  get_evidence: "live",
1551
+ read_episode_file: "live (text files an episode holds: md, txt, yaml, yml, json, up to 512 KiB, paged; read as you, nothing changed)",
1258
1552
  get_evaluations: "live (tracing records and recorded assessments; no provider scores)",
1259
1553
  get_narrative_beats: "live (NCP project beats and candidate beats)",
1260
1554
  review_lookup: "live (video search needs the review token on the connector host)",
@@ -1262,6 +1556,8 @@ export const TOOL_STATUS = {
1262
1556
  handoff_status: "live",
1263
1557
  list_records: "live",
1264
1558
  get_run_result: "live",
1559
+ mission_status: "live (operator details need the Miadi write tier)",
1560
+ provider_capabilities: "live, Miadi write tier",
1265
1561
  search: "live",
1266
1562
  fetch: "live",
1267
1563
  append_observation: "live, contribute",
@@ -1275,7 +1571,8 @@ export const TOOL_STATUS = {
1275
1571
  propose_work: "live, contribute, needs_approval (approving starts the work as a factory run)",
1276
1572
  prepare_calendar_event: "live, contribute (draft and .ics only)",
1277
1573
  propose_session_completion: "live, contribute, needs_approval (decision recorded, nothing closed)",
1278
- request_authorized_run: "live, contribute, needs_approval (queues a factory brief once approved)",
1574
+ request_authorized_run: "live, contribute, needs_approval (queues a factory brief once approved; may name a verified provider and a bounded steering grant)",
1575
+ request_session_steer: "live, contribute, write tier (runs inside an approved run's steering grant, else one scoped human decision)",
1279
1576
  request_review_operation: "live, contribute, needs_approval",
1280
1577
  request_deep_diver: "live, contribute, needs_approval",
1281
1578
  link_review_to_episode: "live, contribute, needs_approval",
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.9";
1
+ export declare const VERSION = "0.1.11";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.1.9";
1
+ export const VERSION = "0.1.11";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miadi/connector",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
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",
@@ -26,7 +26,7 @@
26
26
  "README.md"
27
27
  ],
28
28
  "dependencies": {
29
- "@miadi/inquiry-weave": "0.18.5",
29
+ "@miadi/inquiry-weave": "0.18.8",
30
30
  "@miadi/node-service-kit": "0.1.2",
31
31
  "@modelcontextprotocol/sdk": "^1.30.0",
32
32
  "express": "^5.2.1",