@miadi/connector 0.1.0 → 0.1.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/README.md +6 -3
- package/dist/approve.d.ts +2 -2
- package/dist/approve.js +24 -2
- package/dist/config.d.ts +4 -0
- package/dist/config.js +4 -0
- package/dist/oauth.d.ts +13 -1
- package/dist/oauth.js +11 -1
- package/dist/records.d.ts +1 -1
- package/dist/records.js +2 -0
- package/dist/server.js +19 -2
- package/dist/tools.js +104 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -21,16 +21,17 @@ Call `get_capabilities` first. It names the signed-in person, their Miadi API ti
|
|
|
21
21
|
| kind | tools |
|
|
22
22
|
|---|---|
|
|
23
23
|
| reads | `get_capabilities` `get_context` `list_circles` `get_circle` `list_sessions` `get_session` `get_evidence` `get_evaluations` `get_narrative_beats` `review_lookup` `review_versions` `handoff_status` `list_records` `get_run_result` `search` `fetch` |
|
|
24
|
-
| contributions | `append_observation` `open_inquiry` `link_records` `propose_narrative_beat` `record_evaluation` `register_source_intent` `record_handoff` `update_handoff` `prepare_calendar_event` |
|
|
25
|
-
| requests (approved by a person) | `propose_work` `propose_session_completion` `request_authorized_run` `request_review_operation` `request_deep_diver` `link_review_to_episode` |
|
|
24
|
+
| contributions | `append_observation` `open_inquiry` `link_records` `propose_narrative_beat` `record_evaluation` `register_source_intent` `record_handoff` `update_handoff` `prepare_calendar_event` `save_research` |
|
|
25
|
+
| requests (approved by a person) | `propose_work` `propose_session_completion` `request_authorized_run` `request_review_operation` `request_deep_diver` `link_review_to_episode` `request_inquiry_episode` |
|
|
26
26
|
|
|
27
27
|
- **Reads** stay within the person's Miadi tier. Sessions need the write tier, because a session's name and pane are operator data.
|
|
28
|
-
- **Contributions** need the `miadi:contribute` scope, chosen at sign-in. They append a record to the ledger and change nothing else.
|
|
28
|
+
- **Contributions** need the `miadi:contribute` scope, chosen at sign-in. They append a record to the ledger and change nothing else, except `save_research`: it saves a report on the inquiry shelf as a new folder (the Markdown exactly as given, owner and visibility recorded, private by default), opens the folder's inquiry issue, and commits and pushes the folder in the shelf's repository.
|
|
29
29
|
- **Requests** append a record that waits for approval. The answer gives an `approval_url`. A person with Miadi's write tier opens it in a browser, signs in, and approves or declines. Only that page runs the request, once, as that person:
|
|
30
30
|
- a run becomes a brief in the factory queue, which the intake delivers to a session;
|
|
31
31
|
- a review operation calls the review service;
|
|
32
32
|
- Deep Diver marks the review version ready;
|
|
33
33
|
- an episode link goes through Miadi's episode door;
|
|
34
|
+
- an inquiry is woven into an episode through its inquiry door, or a new episode is minted with it;
|
|
34
35
|
- a session ending is recorded, and no pane is closed.
|
|
35
36
|
|
|
36
37
|
The model that asked cannot approve its own request.
|
|
@@ -53,6 +54,8 @@ MIADI_CONNECTOR_PUBLIC_URL=https://mcp.sanctuaireagentique.com node packages/con
|
|
|
53
54
|
| `MIADI_REVIEW_BASE_URL` | `https://miadi-review-service.vercel.app` | the review service |
|
|
54
55
|
| `MIADI_REVIEW_TOKEN` | read from `~/.env` | review search and approved review operations |
|
|
55
56
|
| `MIADI_FACTORY_DIR` | `~/.miadi/factory` | the queue approved runs are written to |
|
|
57
|
+
| `MIADI_INQUIRY_DIR` | `/a/src/IAIP/prototypes/artefacts` | the inquiry shelf `save_research` writes to, inside a git checkout it pushes |
|
|
58
|
+
| `MIADI_INQUIRY_GITHUB_REPO` | `miadisabelle/Etuaptmumk-RSM` | where a saved report's inquiry issue is opened, with the host's `gh` login |
|
|
56
59
|
|
|
57
60
|
On gaia it runs as the user service `miadi-connector.service`, and the Cloudflare tunnel `gaia` routes `mcp.sanctuaireagentique.com` to it.
|
|
58
61
|
|
package/dist/approve.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The approval door. A request made through the connector (a run, a review
|
|
3
|
-
* operation, Deep Diver, a review-to-episode link,
|
|
4
|
-
* ending) waits here until a person with Miadi's write tier opens
|
|
3
|
+
* operation, Deep Diver, a review-to-episode link, an inquiry-to-episode weave,
|
|
4
|
+
* a work proposal, a session ending) waits here until a person with Miadi's write tier opens
|
|
5
5
|
* /approve/<record id>, signs in with their Miadi person token and approves or
|
|
6
6
|
* declines it. The model that asked cannot approve: the page is a browser form
|
|
7
7
|
* outside the MCP channel.
|
package/dist/approve.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The approval door. A request made through the connector (a run, a review
|
|
3
|
-
* operation, Deep Diver, a review-to-episode link,
|
|
4
|
-
* ending) waits here until a person with Miadi's write tier opens
|
|
3
|
+
* operation, Deep Diver, a review-to-episode link, an inquiry-to-episode weave,
|
|
4
|
+
* a work proposal, a session ending) waits here until a person with Miadi's write tier opens
|
|
5
5
|
* /approve/<record id>, signs in with their Miadi person token and approves or
|
|
6
6
|
* declines it. The model that asked cannot approve: the page is a browser form
|
|
7
7
|
* outside the MCP channel.
|
|
@@ -24,6 +24,7 @@ const APPROVABLE = new Set([
|
|
|
24
24
|
"review_episode_link_request",
|
|
25
25
|
"work_proposal",
|
|
26
26
|
"session_completion_proposal",
|
|
27
|
+
"inquiry_episode_request",
|
|
27
28
|
]);
|
|
28
29
|
export function factoryDir(env = process.env) {
|
|
29
30
|
return env.MIADI_FACTORY_DIR || join(homedir(), ".miadi", "factory");
|
|
@@ -158,6 +159,27 @@ export async function executeApproved(record, approver, deps) {
|
|
|
158
159
|
throw e;
|
|
159
160
|
}
|
|
160
161
|
}
|
|
162
|
+
case "inquiry_episode_request": {
|
|
163
|
+
// A new episode is minted with the inquiry woven in one call; an existing one goes through its inquiry door.
|
|
164
|
+
const inquiry = { artefact: b.artefact, ...(b.issue ? { issue: b.issue } : {}) };
|
|
165
|
+
try {
|
|
166
|
+
if (b.new_episode) {
|
|
167
|
+
const res = await deps.miadi.request(approver.token, "POST", "/api/chronicle/episodes", {
|
|
168
|
+
title: b.new_episode.title,
|
|
169
|
+
...(b.new_episode.goal ? { goal: b.new_episode.goal } : {}),
|
|
170
|
+
inquiry,
|
|
171
|
+
});
|
|
172
|
+
return { outcome: "minted_and_woven", response: res };
|
|
173
|
+
}
|
|
174
|
+
const res = await deps.miadi.request(approver.token, "POST", `/api/chronicle/episodes/${encodeURIComponent(b.episode)}/inquiry`, inquiry);
|
|
175
|
+
return { outcome: "woven", response: res };
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
if (e instanceof MiadiError)
|
|
179
|
+
return { outcome: "failed", status: e.status, response: e.body };
|
|
180
|
+
throw e;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
161
183
|
case "work_proposal":
|
|
162
184
|
return { outcome: "approved", note: "The proposal is approved; a run starts only through request_authorized_run." };
|
|
163
185
|
case "session_completion_proposal":
|
package/dist/config.d.ts
CHANGED
|
@@ -10,5 +10,9 @@ export interface ConnectorConfig {
|
|
|
10
10
|
reviewUrl: string;
|
|
11
11
|
reviewToken?: string;
|
|
12
12
|
stateDir: string;
|
|
13
|
+
/** The inquiry shelf research reports are saved on (MIADI_INQUIRY_DIR). */
|
|
14
|
+
inquiryRoot?: string;
|
|
15
|
+
/** owner/repo a saved report's inquiry issue is opened in (MIADI_INQUIRY_GITHUB_REPO). */
|
|
16
|
+
inquiryRepo?: string;
|
|
13
17
|
}
|
|
14
18
|
export declare function configFromEnv(env?: NodeJS.ProcessEnv): ConnectorConfig;
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { resolveEnv } from "@miadi/inquiry-weave";
|
|
1
2
|
import { reviewToken } from "./reviews.js";
|
|
2
3
|
export function configFromEnv(env = process.env) {
|
|
3
4
|
const port = Number(env.MIADI_CONNECTOR_PORT || 3337);
|
|
5
|
+
const shelf = resolveEnv({}, env);
|
|
4
6
|
return {
|
|
5
7
|
publicUrl: (env.MIADI_CONNECTOR_PUBLIC_URL || `http://127.0.0.1:${port}`).replace(/\/$/, ""),
|
|
6
8
|
host: env.MIADI_CONNECTOR_HOST || "127.0.0.1",
|
|
@@ -10,5 +12,7 @@ export function configFromEnv(env = process.env) {
|
|
|
10
12
|
reviewUrl: (env.MIADI_REVIEW_BASE_URL || "https://miadi-review-service.vercel.app").replace(/\/$/, ""),
|
|
11
13
|
reviewToken: reviewToken(env),
|
|
12
14
|
stateDir: env.MIADI_CONNECTOR_STATE_DIR || "/srv/miadi/connector",
|
|
15
|
+
inquiryRoot: shelf.inquiryRoot,
|
|
16
|
+
inquiryRepo: shelf.repo,
|
|
13
17
|
};
|
|
14
18
|
}
|
package/dist/oauth.d.ts
CHANGED
|
@@ -60,9 +60,21 @@ export declare const emptyOAuthState: () => OAuthState;
|
|
|
60
60
|
export declare class MiadiOAuthProvider implements OAuthServerProvider {
|
|
61
61
|
private readonly state;
|
|
62
62
|
private readonly identify;
|
|
63
|
+
/**
|
|
64
|
+
* The issuer as the metadata states it. It goes back on every authorization
|
|
65
|
+
* response as `iss` (RFC 9207), which ChatGPT requires to use its stable
|
|
66
|
+
* callback URL.
|
|
67
|
+
*/
|
|
68
|
+
private readonly issuer?;
|
|
63
69
|
private readonly confirmPath;
|
|
64
70
|
private readonly direct;
|
|
65
|
-
constructor(state: JsonFile<OAuthState>, identify: Identify,
|
|
71
|
+
constructor(state: JsonFile<OAuthState>, identify: Identify,
|
|
72
|
+
/**
|
|
73
|
+
* The issuer as the metadata states it. It goes back on every authorization
|
|
74
|
+
* response as `iss` (RFC 9207), which ChatGPT requires to use its stable
|
|
75
|
+
* callback URL.
|
|
76
|
+
*/
|
|
77
|
+
issuer?: string | undefined, confirmPath?: string);
|
|
66
78
|
get clientsStore(): OAuthRegisteredClientsStore;
|
|
67
79
|
authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
|
|
68
80
|
/** POST from the sign-in page: the person's token, or a refusal. */
|
package/dist/oauth.js
CHANGED
|
@@ -14,11 +14,19 @@ const now = () => Math.floor(Date.now() / 1000);
|
|
|
14
14
|
export class MiadiOAuthProvider {
|
|
15
15
|
state;
|
|
16
16
|
identify;
|
|
17
|
+
issuer;
|
|
17
18
|
confirmPath;
|
|
18
19
|
direct = new Map();
|
|
19
|
-
constructor(state, identify,
|
|
20
|
+
constructor(state, identify,
|
|
21
|
+
/**
|
|
22
|
+
* The issuer as the metadata states it. It goes back on every authorization
|
|
23
|
+
* response as `iss` (RFC 9207), which ChatGPT requires to use its stable
|
|
24
|
+
* callback URL.
|
|
25
|
+
*/
|
|
26
|
+
issuer, confirmPath = "/authorize/confirm") {
|
|
20
27
|
this.state = state;
|
|
21
28
|
this.identify = identify;
|
|
29
|
+
this.issuer = issuer;
|
|
22
30
|
this.confirmPath = confirmPath;
|
|
23
31
|
}
|
|
24
32
|
get clientsStore() {
|
|
@@ -68,6 +76,8 @@ export class MiadiOAuthProvider {
|
|
|
68
76
|
const redirect = new URL(pending.params.redirectUri);
|
|
69
77
|
if (pending.params.state)
|
|
70
78
|
redirect.searchParams.set("state", pending.params.state);
|
|
79
|
+
if (this.issuer)
|
|
80
|
+
redirect.searchParams.set("iss", this.issuer);
|
|
71
81
|
if (body.decision === "deny") {
|
|
72
82
|
this.state.update((st) => delete st.pending[body.request_id]);
|
|
73
83
|
redirect.searchParams.set("error", "access_denied");
|
package/dist/records.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export declare const DOMAINS: readonly ["creative", "ceremonial", "engineering"]
|
|
|
35
35
|
/** #815 "claim category": what kind of statement a record makes. */
|
|
36
36
|
export declare const CLAIM_CATEGORIES: readonly ["observed", "hypothesis", "desired_future", "fiction", "verified_implementation"];
|
|
37
37
|
export type ClaimCategory = (typeof CLAIM_CATEGORIES)[number];
|
|
38
|
-
export declare const RECORD_KINDS: readonly ["observation", "inquiry", "link", "work_proposal", "run_request", "handoff", "source_intent", "review_operation_request", "deep_diver_request", "review_episode_link_request", "calendar_event_draft", "session_completion_proposal"];
|
|
38
|
+
export declare const RECORD_KINDS: readonly ["observation", "inquiry", "link", "work_proposal", "run_request", "handoff", "source_intent", "review_operation_request", "deep_diver_request", "review_episode_link_request", "calendar_event_draft", "session_completion_proposal", "research_artifact", "inquiry_episode_request"];
|
|
39
39
|
export type RecordKind = (typeof RECORD_KINDS)[number];
|
|
40
40
|
/** #816 handoff states. */
|
|
41
41
|
export declare const HANDOFF_STATES: readonly ["pending", "blocked", "ready", "in_progress", "completed", "withdrawn"];
|
package/dist/records.js
CHANGED
|
@@ -58,6 +58,8 @@ export const RECORD_KINDS = [
|
|
|
58
58
|
"review_episode_link_request",
|
|
59
59
|
"calendar_event_draft",
|
|
60
60
|
"session_completion_proposal",
|
|
61
|
+
"research_artifact",
|
|
62
|
+
"inquiry_episode_request",
|
|
61
63
|
];
|
|
62
64
|
/** #816 handoff states. */
|
|
63
65
|
export const HANDOFF_STATES = ["pending", "blocked", "ready", "in_progress", "completed", "withdrawn"];
|
package/dist/server.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import express from "express";
|
|
13
13
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
14
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
15
|
-
import { mcpAuthRouter, getOAuthProtectedResourceMetadataUrl } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
15
|
+
import { mcpAuthRouter, createOAuthMetadata, getOAuthProtectedResourceMetadataUrl } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
16
16
|
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
|
|
17
17
|
import { MiadiOAuthProvider, SCOPES, emptyOAuthState } from "./oauth.js";
|
|
18
18
|
import { registerApprovalRoutes } from "./approve.js";
|
|
@@ -26,7 +26,9 @@ export function createConnectorApp(config, deps) {
|
|
|
26
26
|
const paths = statePaths(config.stateDir);
|
|
27
27
|
const oauthState = new JsonFile(paths.oauth, emptyOAuthState);
|
|
28
28
|
const ledger = new Ledger(paths.ledger);
|
|
29
|
-
|
|
29
|
+
// The issuer string exactly as the metadata states it (URL.href, so with its trailing slash).
|
|
30
|
+
const issuer = new URL(config.publicUrl).href;
|
|
31
|
+
const provider = new MiadiOAuthProvider(oauthState, deps.identify, issuer);
|
|
30
32
|
const miadi = new MiadiClient(config.miadiUrl, deps.fetchImpl);
|
|
31
33
|
const reviews = new ReviewClient(config.reviewUrl, config.reviewToken, deps.fetchImpl);
|
|
32
34
|
const publicUrl = new URL(config.publicUrl);
|
|
@@ -38,6 +40,21 @@ export function createConnectorApp(config, deps) {
|
|
|
38
40
|
app.get("/health", (_req, res) => {
|
|
39
41
|
res.json({ ok: true, service: "@miadi/connector", version: VERSION });
|
|
40
42
|
});
|
|
43
|
+
// The SDK's metadata, plus RFC 9207: authorization responses carry `iss`.
|
|
44
|
+
// Served before the SDK router so this document is the one clients read.
|
|
45
|
+
const asMetadata = {
|
|
46
|
+
...createOAuthMetadata({
|
|
47
|
+
provider,
|
|
48
|
+
issuerUrl: publicUrl,
|
|
49
|
+
baseUrl: publicUrl,
|
|
50
|
+
scopesSupported: SCOPES,
|
|
51
|
+
serviceDocumentationUrl: new URL("https://github.com/jgwill/Miadi/tree/main/packages/connector"),
|
|
52
|
+
}),
|
|
53
|
+
authorization_response_iss_parameter_supported: true,
|
|
54
|
+
};
|
|
55
|
+
app.get("/.well-known/oauth-authorization-server", (_req, res) => {
|
|
56
|
+
res.set("cache-control", "no-store").json(asMetadata);
|
|
57
|
+
});
|
|
41
58
|
app.use(mcpAuthRouter({
|
|
42
59
|
provider,
|
|
43
60
|
issuerUrl: publicUrl,
|
package/dist/tools.js
CHANGED
|
@@ -17,6 +17,8 @@ import { SCOPE_CONTRIBUTE } from "./oauth.js";
|
|
|
17
17
|
import { CIRCLES, CIRCLE_IDS, CLAIM_CATEGORIES, DOMAINS, HANDOFF_STATES, current, newRecordId, } from "./records.js";
|
|
18
18
|
import { VERSION } from "./version.js";
|
|
19
19
|
import { readQueueState } from "./approve.js";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { artefactVisibility, readArtefactWeave, saveResearchArtefact } from "@miadi/inquiry-weave";
|
|
20
22
|
class ToolRefusal extends Error {
|
|
21
23
|
code;
|
|
22
24
|
detail;
|
|
@@ -872,6 +874,106 @@ export function registerTools(server, ctx) {
|
|
|
872
874
|
});
|
|
873
875
|
return { ...r, approval_url: approvalUrl(r.id) };
|
|
874
876
|
}));
|
|
877
|
+
// ── research on the inquiry shelf (jgwill/Miadi#879) ───────────────────
|
|
878
|
+
server.registerTool("save_research", {
|
|
879
|
+
title: "Save a research report on the inquiry shelf",
|
|
880
|
+
description: "Save a finished research report (for example a Deep Research export) in your name: a new folder on Miadi's inquiry shelf holding the Markdown exactly as given, its inquiry issue, and the folder committed and pushed in the shelf's repository. Private to you unless shared with a circle; publishing goes through the public-inquiry route, never here. The same report saved again returns the folder already saved. Weave it into an episode afterwards with request_inquiry_episode.",
|
|
881
|
+
inputSchema: {
|
|
882
|
+
title: z.string().min(1).max(200),
|
|
883
|
+
markdown: z.string().min(1).max(1_000_000).describe("The whole report, as Markdown. Saved byte-for-byte."),
|
|
884
|
+
refs: refs.describe("Records and sources the report answers: a connector inquiry id, a DOI or URL, owner/repo#n."),
|
|
885
|
+
visibility: z.enum(["private", "circle"]).default("private"),
|
|
886
|
+
circle: circle.optional().describe("The circle it is shared with, when visibility is circle."),
|
|
887
|
+
...base,
|
|
888
|
+
},
|
|
889
|
+
annotations: WRITE,
|
|
890
|
+
}, wrap(async (a, extra) => {
|
|
891
|
+
const { person, auth } = personOf(extra);
|
|
892
|
+
requireContribute(auth);
|
|
893
|
+
if (!config.inquiryRoot || !config.inquiryRepo)
|
|
894
|
+
throw new ToolRefusal("unavailable", "This connector host has no inquiry shelf configured.");
|
|
895
|
+
if (a.visibility === "circle" && !a.circle)
|
|
896
|
+
throw new ToolRefusal("invalid_input", "Name the circle the report is shared with.");
|
|
897
|
+
const saved = await saveResearchArtefact({
|
|
898
|
+
inquiryRoot: config.inquiryRoot,
|
|
899
|
+
repo: config.inquiryRepo,
|
|
900
|
+
title: a.title,
|
|
901
|
+
markdown: a.markdown,
|
|
902
|
+
owner: { person_id: person.id, name: person.name },
|
|
903
|
+
visibility: a.visibility,
|
|
904
|
+
circle: a.circle,
|
|
905
|
+
via: "miadi-connector",
|
|
906
|
+
refs: a.refs,
|
|
907
|
+
});
|
|
908
|
+
const r = append("research_artifact", person, auth, {
|
|
909
|
+
circle: a.circle ?? "stewardship",
|
|
910
|
+
refs: [`inquiry-artefact:${saved.artefact}`, ...(saved.issue ? [saved.issue] : []), ...a.refs],
|
|
911
|
+
intention: a.intention,
|
|
912
|
+
role: a.role,
|
|
913
|
+
idempotency_key: a.idempotency_key ?? `research:${saved.sha256}`,
|
|
914
|
+
claim_category: "observed",
|
|
915
|
+
state: saved.shelf.state === "landed" ? "completed" : "blocked",
|
|
916
|
+
next_action: "request_inquiry_episode to weave it into an episode or start one",
|
|
917
|
+
body: {
|
|
918
|
+
title: a.title,
|
|
919
|
+
artefact: saved.artefact,
|
|
920
|
+
issue: saved.issue,
|
|
921
|
+
issue_url: saved.issue_url,
|
|
922
|
+
sha256: saved.sha256,
|
|
923
|
+
bytes: saved.bytes,
|
|
924
|
+
visibility: saved.visibility,
|
|
925
|
+
circle: a.circle,
|
|
926
|
+
shelf: { state: saved.shelf.state, observed: saved.shelf.observed },
|
|
927
|
+
},
|
|
928
|
+
});
|
|
929
|
+
return { ...r, saved: saved.status, research_url: `${config.miadiPublicUrl}/research` };
|
|
930
|
+
}));
|
|
931
|
+
server.registerTool("request_inquiry_episode", {
|
|
932
|
+
title: "Request that an inquiry be woven into an episode, or start one",
|
|
933
|
+
description: "Ask that a folder on the inquiry shelf (from save_research) be woven into a Chronicle episode, or that a new episode be started from it. Weaving copies the folder into the episode, so the approval page shows its visibility. Waits for a person's approval; on approval it runs once through Miadi's episode doors.",
|
|
934
|
+
inputSchema: {
|
|
935
|
+
artefact: z.string().min(1).max(300).describe("The shelf folder name save_research returned."),
|
|
936
|
+
episode: z.string().min(1).optional().describe("An existing episode: miadi-chronicle://<n>, a number or a folder."),
|
|
937
|
+
new_episode_title: z.string().min(1).max(200).optional().describe("Start a new episode with this title instead."),
|
|
938
|
+
goal: z.string().max(2000).optional().describe("The new episode's goal."),
|
|
939
|
+
...base,
|
|
940
|
+
},
|
|
941
|
+
annotations: WRITE,
|
|
942
|
+
}, wrap(async (a, extra) => {
|
|
943
|
+
const { person, auth } = personOf(extra);
|
|
944
|
+
requireContribute(auth);
|
|
945
|
+
if (Boolean(a.episode) === Boolean(a.new_episode_title)) {
|
|
946
|
+
throw new ToolRefusal("invalid_input", "Name either an existing episode or a new episode title, not both.");
|
|
947
|
+
}
|
|
948
|
+
if (!config.inquiryRoot)
|
|
949
|
+
throw new ToolRefusal("unavailable", "This connector host has no inquiry shelf configured.");
|
|
950
|
+
const name = String(a.artefact).trim();
|
|
951
|
+
if (!/^[\w.-]+$/.test(name))
|
|
952
|
+
throw new ToolRefusal("invalid_ref", "Give the bare shelf folder name.");
|
|
953
|
+
const weave = await readArtefactWeave(join(config.inquiryRoot, name)).catch(() => undefined);
|
|
954
|
+
if (!weave)
|
|
955
|
+
throw new ToolRefusal("not_found", `No folder ${name} on the inquiry shelf.`);
|
|
956
|
+
if (weave.owner && weave.owner.person_id !== person.id && person.api_tier !== "write") {
|
|
957
|
+
throw new ToolRefusal("forbidden", "That folder belongs to another person.");
|
|
958
|
+
}
|
|
959
|
+
const episode = a.episode ? String(a.episode).replace(/^miadi-chronicle:(\/\/)?/, "").trim() : undefined;
|
|
960
|
+
const r = append("inquiry_episode_request", person, auth, {
|
|
961
|
+
circle: "continuity",
|
|
962
|
+
refs: [`inquiry-artefact:${name}`, ...(weave.issue ? [weave.issue] : []), ...(episode ? [`miadi-chronicle://${episode}`] : [])],
|
|
963
|
+
intention: a.intention,
|
|
964
|
+
role: a.role,
|
|
965
|
+
idempotency_key: a.idempotency_key ?? `${name}:${episode ?? `new:${a.new_episode_title}`}`,
|
|
966
|
+
approval_state: "awaiting_approval",
|
|
967
|
+
body: {
|
|
968
|
+
artefact: name,
|
|
969
|
+
issue: weave.issue,
|
|
970
|
+
visibility: artefactVisibility(weave),
|
|
971
|
+
owner: weave.owner?.name ?? weave.owner?.person_id ?? "no owner recorded",
|
|
972
|
+
...(episode ? { episode } : { new_episode: { title: a.new_episode_title, ...(a.goal ? { goal: a.goal } : {}) } }),
|
|
973
|
+
},
|
|
974
|
+
});
|
|
975
|
+
return { ...r, approval_url: approvalUrl(r.id) };
|
|
976
|
+
}));
|
|
875
977
|
}
|
|
876
978
|
function asState(e) {
|
|
877
979
|
if (e instanceof MiadiError)
|
|
@@ -951,4 +1053,6 @@ export const TOOL_STATUS = {
|
|
|
951
1053
|
request_review_operation: "live, contribute, needs_approval",
|
|
952
1054
|
request_deep_diver: "live, contribute, needs_approval",
|
|
953
1055
|
link_review_to_episode: "live, contribute, needs_approval",
|
|
1056
|
+
save_research: "live, contribute (saves on the inquiry shelf, opens its issue, pushes; private by default)",
|
|
1057
|
+
request_inquiry_episode: "live, contribute, needs_approval (weaves a shelf folder into an episode or starts one)",
|
|
954
1058
|
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.2";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.1.
|
|
1
|
+
export const VERSION = "0.1.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miadi/connector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"README.md"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
+
"@miadi/inquiry-weave": "0.18.4",
|
|
25
26
|
"@miadi/node-service-kit": "0.1.2",
|
|
26
27
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
27
28
|
"express": "^5.2.1",
|