@miadi/connector 0.1.20 → 0.1.22
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 +58 -4
- package/dist/approve.d.ts +11 -1
- package/dist/approve.js +97 -9
- package/dist/bin.js +3 -0
- package/dist/decisions.d.ts +2 -2
- package/dist/decisions.js +96 -3
- package/dist/draft-review.d.ts +96 -0
- package/dist/draft-review.js +267 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mission-standard.d.ts +65 -0
- package/dist/mission-standard.js +178 -0
- package/dist/mission-status.d.ts +13 -1
- package/dist/mission-status.js +10 -4
- package/dist/model-preflight.d.ts +1 -1
- package/dist/records.d.ts +1 -1
- package/dist/records.js +2 -0
- package/dist/request-state.js +6 -1
- package/dist/research-digest.d.ts +30 -0
- package/dist/research-digest.js +104 -0
- package/dist/research-shelf.d.ts +173 -0
- package/dist/research-shelf.js +297 -0
- package/dist/runtime-steer.d.ts +182 -0
- package/dist/runtime-steer.js +543 -0
- package/dist/server.d.ts +20 -1
- package/dist/server.js +7 -11
- package/dist/steer.d.ts +21 -7
- package/dist/steer.js +115 -10
- package/dist/tools.d.ts +32 -1
- package/dist/tools.js +326 -46
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -2
package/dist/tools.js
CHANGED
|
@@ -23,7 +23,9 @@ import { discoverProviders, jevConfigOf, resolveProvider, routerPreflight, PROVI
|
|
|
23
23
|
import { authorizesActions, missionStatus, primaryIssueOf, resolveMissionSubject } from "./mission-status.js";
|
|
24
24
|
import { authorizationReceipt, authorizeProvider, CHOICE_PROVIDERS, isPersonId, needsPersonChoice, providerPolicyOf } from "./provider-policy.js";
|
|
25
25
|
import { candidatesFor, CRITICALITIES, jevAdapter, modelPreflight, PREFLIGHT_CONTRACT, PREFLIGHT_VERSION, TASK_TYPES, } from "./model-preflight.js";
|
|
26
|
-
import {
|
|
26
|
+
import { ShelfRefusal, listShelfArtefacts, readShelfArtefact } from "./research-shelf.js";
|
|
27
|
+
import { CLAUDE_ADAPTER, DELIVERY_STATES, readSteerDelivery, revokeInInbox, settlementOf } from "./runtime-steer.js";
|
|
28
|
+
import { checkGrant, isBusy, steerAttempts, steeringGrantOf, steeringRevoked, steerSession } from "./steer.js";
|
|
27
29
|
import { factoryLedgerPath, readFactoryReceipts, receiptsForRecord, recordsForSession } from "./factory-receipt.js";
|
|
28
30
|
import { missionResult, redactResult } from "./mission-result.js";
|
|
29
31
|
import { factoryDir as defaultFactoryDir } from "./queue-state.js";
|
|
@@ -35,6 +37,18 @@ import { canReadReading, readingOwner } from "./reading/privacy.js";
|
|
|
35
37
|
import { artefactVisibility, readArtefactWeave, saveResearchArtefact } from "@miadi/inquiry-weave";
|
|
36
38
|
import { ResearchUploads, UploadRefusal } from "./uploads.js";
|
|
37
39
|
import { DEFAULT_PAGE_BYTES, FOUNDATIONS_PERMISSION, FileRefusal, MAX_TEXT_BYTES, MIN_PAGE_BYTES, TEXT_TYPES, checkRelativePath, episodeNumberOf, extensionOf, fileRef, holdsFoundationRead, isFoundationDoc, pageOf, parseFileRef, } from "./episode-files.js";
|
|
40
|
+
/** Which standard judges a mission now, and whether Jev takes part; no key value is read into it. */
|
|
41
|
+
async function standardStatus(service) {
|
|
42
|
+
const standard = await service.standard();
|
|
43
|
+
return {
|
|
44
|
+
name: standard.name,
|
|
45
|
+
version: standard.version,
|
|
46
|
+
source: standard.source,
|
|
47
|
+
...(standard.note ? { note: standard.note } : {}),
|
|
48
|
+
aspects: standard.aspects.map((a) => ({ code: a.code, name: a.name, requires: a.requires, settled_by: a.settled_by })),
|
|
49
|
+
jev: service.jevConfigured ? "configured" : "unconfigured",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
38
52
|
class ToolRefusal extends Error {
|
|
39
53
|
code;
|
|
40
54
|
detail;
|
|
@@ -99,6 +113,8 @@ const primaryIssue = z
|
|
|
99
113
|
.regex(/^[\w.-]+\/[\w.-]+#\d+$/)
|
|
100
114
|
.optional()
|
|
101
115
|
.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).");
|
|
116
|
+
const deliveryField = z.string().min(1).max(500).optional().describe("The one exact place the result lands: a path, a branch, a pull request, an issue, a review or an episode. Not a kind of place or a choice among places (jgwill/Miadi#939).");
|
|
117
|
+
const personWordsField = z.string().min(1).max(4000).optional().describe("The person's own words, verbatim from your conversation, for any rule, correction or decision the mission credits to them. The person reads them on the card and the worker reads them in the brief.");
|
|
102
118
|
/** Refs with the declared primary issue first, never duplicated. */
|
|
103
119
|
function withPrimary(refs, primary) {
|
|
104
120
|
return primary ? [primary, ...refs.filter((r) => r !== primary)] : refs;
|
|
@@ -150,6 +166,27 @@ export function githubGetter(fetchImpl = fetch, token = process.env.GITHUB_TOKEN
|
|
|
150
166
|
};
|
|
151
167
|
}
|
|
152
168
|
/** Whether the steering transport works now: registered is not operational (jgwill/Miadi#909). */
|
|
169
|
+
/**
|
|
170
|
+
* The provider interface as a client reads it (jgwill/Miadi#930). A correction goes to the session's
|
|
171
|
+
* own runtime, and busy is normal. Only the adapter named here exists. No other provider is assumed
|
|
172
|
+
* to behave like Claude Code.
|
|
173
|
+
*/
|
|
174
|
+
/** A steer being sent now, by its record: a retry arriving during it waits for this answer and sends nothing (jgwill/Miadi#930). */
|
|
175
|
+
const steersInFlight = new Map();
|
|
176
|
+
export const STEERING_CONTRACT = {
|
|
177
|
+
busy_is_normal: true,
|
|
178
|
+
connector: "Admits the steer, resolves mission → session → runtime, hands it to that session's inbox and reports what the runtime accepted. It does not watch tools or wait for idle.",
|
|
179
|
+
runtime: "The agent's own lifecycle presents it to its model at the next supported continuation in the same turn.",
|
|
180
|
+
delivery_states: DELIVERY_STATES,
|
|
181
|
+
provider_adapters: {
|
|
182
|
+
claude: { route: "claude_hook_inbox", continuation: "PostToolUse, PostToolUseFailure, then Stop if no tool follows", needs: `${CLAUDE_ADAPTER}, loaded when the session started` },
|
|
183
|
+
codex: { route: "unsupported" },
|
|
184
|
+
copilot: { route: "unsupported" },
|
|
185
|
+
pi: { route: "unsupported" },
|
|
186
|
+
gemini: { route: "unsupported" },
|
|
187
|
+
},
|
|
188
|
+
idle_seat: "An idle seat has no continuation coming, so its steer is typed into its bound pane once the prompt reads empty.",
|
|
189
|
+
};
|
|
153
190
|
export function steeringTransport(tmux) {
|
|
154
191
|
if (!tmux)
|
|
155
192
|
return { tool: "registered", transport: "unavailable", reason: "The connector host was started without a steering surface." };
|
|
@@ -232,8 +269,44 @@ export function registerTools(server, ctx) {
|
|
|
232
269
|
...fields,
|
|
233
270
|
});
|
|
234
271
|
};
|
|
272
|
+
/** A person's standing preferences are read by that person and by the write tier. */
|
|
273
|
+
const reviewShownTo = (review, r, reader) => review ? (reader.api_tier === "write" || r.actor.person_id === reader.id ? review : { ...review, preferences: [] }) : null;
|
|
274
|
+
/** The report for a mission's current version; a failure here never refuses the request (jgwill/Miadi#939). */
|
|
275
|
+
const draftReviewOf = async (r) => ctx.draftReview ? ctx.draftReview.review(all().find((x) => x.id === r.id) ?? r, all()) : { state: "unavailable", message: "This connector runs no draft review." };
|
|
276
|
+
/**
|
|
277
|
+
* A forwarded steer's record moves when the runtime's evidence moves: done once the model has it,
|
|
278
|
+
* stopped once it never will. The ledger then agrees with its delivery on every surface that reads it
|
|
279
|
+
* (jgwill/Miadi#930). This only reads the inbox and the transcript, and appends when something changed.
|
|
280
|
+
*/
|
|
281
|
+
const settleSteers = () => {
|
|
282
|
+
if (!ctx.steeringRuntime)
|
|
283
|
+
return;
|
|
284
|
+
for (const r of current(ledger.all())) {
|
|
285
|
+
if (r.kind !== "session_steer_request")
|
|
286
|
+
continue;
|
|
287
|
+
const settled = settlementOf(ctx.steeringRuntime, r);
|
|
288
|
+
if (!settled)
|
|
289
|
+
continue;
|
|
290
|
+
ledger.append({
|
|
291
|
+
id: newRecordId(r.kind),
|
|
292
|
+
kind: r.kind,
|
|
293
|
+
created_at: new Date().toISOString(),
|
|
294
|
+
actor: r.actor,
|
|
295
|
+
refs: r.refs,
|
|
296
|
+
evidence_refs: r.evidence_refs,
|
|
297
|
+
supersedes: r.id,
|
|
298
|
+
...(r.approval_state ? { approval_state: r.approval_state } : {}),
|
|
299
|
+
state: settled.state,
|
|
300
|
+
completion_receipt: { ...r.completion_receipt, outcome: settled.outcome, delivery_state: settled.outcome, settled_at: new Date().toISOString(), ...(settled.at ? { delivered_at: settled.at } : {}), message: settled.message },
|
|
301
|
+
body: r.body,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
};
|
|
235
305
|
/** Every record, folded; for logic. What a tool returns goes through `shown`. */
|
|
236
|
-
const all = () =>
|
|
306
|
+
const all = () => {
|
|
307
|
+
settleSteers();
|
|
308
|
+
return current(ledger.all());
|
|
309
|
+
};
|
|
237
310
|
const records = () => {
|
|
238
311
|
const raw = all();
|
|
239
312
|
// Completion results can contain exact drafts too. Hide their parent mission as
|
|
@@ -316,8 +389,9 @@ export function registerTools(server, ctx) {
|
|
|
316
389
|
ledger: "connector ledger (append-only, /srv/miadi/connector)",
|
|
317
390
|
},
|
|
318
391
|
tools: TOOL_STATUS,
|
|
392
|
+
mission_standard: ctx.draftReview ? await standardStatus(ctx.draftReview) : { state: "unavailable" },
|
|
319
393
|
// Registered is not operational: whether a steer can reach a pane on this host now (jgwill/Miadi#909).
|
|
320
|
-
steering: steeringTransport(ctx.tmux),
|
|
394
|
+
steering: { ...steeringTransport(ctx.tmux), runtime_route: ctx.steeringRuntime ? "configured" : "not_configured", contract: STEERING_CONTRACT },
|
|
321
395
|
foundation_read: {
|
|
322
396
|
permission: FOUNDATIONS_PERMISSION,
|
|
323
397
|
held: foundations,
|
|
@@ -769,8 +843,11 @@ export function registerTools(server, ctx) {
|
|
|
769
843
|
...(versionOf(r) > 1 ? { history: "Earlier versions: get_request_history." } : {}),
|
|
770
844
|
approval_url: r.approval_state === "awaiting_approval" ? approvalUrl(r.id) : undefined,
|
|
771
845
|
queue: r.kind === "run_request" || r.kind === "work_proposal" ? readQueueState(r, ctx.factoryDir) : undefined,
|
|
846
|
+
// Where a steer stands, joined to its id: operator detail, as mission_status has it (jgwill/Miadi#930).
|
|
847
|
+
...(r.kind === "session_steer_request" && ctx.steeringRuntime && person.api_tier === "write" ? { delivery: readSteerDelivery(ctx.steeringRuntime, all().find((x) => x.id === r.id) ?? r) ?? undefined } : {}),
|
|
772
848
|
// Where the request stands, in the words every surface uses (jgwill/Miadi#889).
|
|
773
849
|
projection: requestProjection(r, { reviewBase: ctx.approval?.config.reviewUrl, factoryDir: ctx.factoryDir }),
|
|
850
|
+
...(r.kind === "run_request" || r.kind === "work_proposal" ? { draft_review: reviewShownTo(ctx.draftReview?.stored(r), r, person) } : {}),
|
|
774
851
|
...(r.approval_state === "revision_requested"
|
|
775
852
|
? { revision: { instructions: r.completion_receipt?.instructions, next: `Revise it in place: revise_pending_request with request_id ${r.id} and expected_version ${versionOf(r)}.` } }
|
|
776
853
|
: {}),
|
|
@@ -794,6 +871,14 @@ export function registerTools(server, ctx) {
|
|
|
794
871
|
return withProviderAuthorization(await missionStatus(ref.trim(), records(), {
|
|
795
872
|
projectOf: (r) => requestProjection(r, { reviewBase: ctx.approval?.config.reviewUrl, factoryDir: ctx.factoryDir }),
|
|
796
873
|
queueOf: (r) => (r.kind === "run_request" || r.kind === "work_proposal" ? readQueueState(r, ctx.factoryDir) : undefined),
|
|
874
|
+
...(writeTier && ctx.steeringRuntime
|
|
875
|
+
? {
|
|
876
|
+
steerDeliveryOf: (r) => {
|
|
877
|
+
const stored = all().find((x) => x.id === r.id) ?? r;
|
|
878
|
+
return readSteerDelivery(ctx.steeringRuntime, stored);
|
|
879
|
+
},
|
|
880
|
+
}
|
|
881
|
+
: {}),
|
|
797
882
|
// Without a lister the host cannot say which sessions live: unknown, never "absent" (jgwill/Miadi#909).
|
|
798
883
|
tmuxSessions: writeTier ? (ctx.tmuxSessions ?? (ctx.tmux?.listSessions ? () => ctx.tmux.listSessions() : undefined)) : undefined,
|
|
799
884
|
paneBusy: writeTier && ctx.tmux
|
|
@@ -1226,6 +1311,8 @@ export function registerTools(server, ctx) {
|
|
|
1226
1311
|
refs,
|
|
1227
1312
|
primary_issue: primaryIssue,
|
|
1228
1313
|
repo: z.string().max(120).optional(),
|
|
1314
|
+
delivery: deliveryField,
|
|
1315
|
+
person_words: personWordsField,
|
|
1229
1316
|
...base,
|
|
1230
1317
|
},
|
|
1231
1318
|
annotations: WRITE,
|
|
@@ -1240,9 +1327,16 @@ export function registerTools(server, ctx) {
|
|
|
1240
1327
|
idempotency_key: a.idempotency_key,
|
|
1241
1328
|
state: "pending",
|
|
1242
1329
|
approval_state: "awaiting_approval",
|
|
1243
|
-
body: {
|
|
1330
|
+
body: {
|
|
1331
|
+
title: a.title,
|
|
1332
|
+
desired_outcome: a.desired_outcome,
|
|
1333
|
+
repo: a.repo,
|
|
1334
|
+
...(a.primary_issue ? { primary_issue: a.primary_issue } : {}),
|
|
1335
|
+
...(a.delivery ? { delivery: a.delivery } : {}),
|
|
1336
|
+
...(a.person_words ? { person_words: a.person_words } : {}),
|
|
1337
|
+
},
|
|
1244
1338
|
});
|
|
1245
|
-
return { ...r, approval_url: approvalUrl(r.id) };
|
|
1339
|
+
return { ...r, approval_url: approvalUrl(r.id), draft_review: await draftReviewOf(r) };
|
|
1246
1340
|
}));
|
|
1247
1341
|
server.registerTool("prepare_calendar_event", {
|
|
1248
1342
|
title: "Draft a calendar event",
|
|
@@ -1409,10 +1503,12 @@ export function registerTools(server, ctx) {
|
|
|
1409
1503
|
title: "Request a bounded agent run",
|
|
1410
1504
|
description: "Ask for an agent session to do one bounded task. The request waits for a person with Miadi's write tier to approve it on the returned approval_url; once approved, a brief is queued for the factory intake, which opens or reaches the session within minutes. Read it back with get_run_result. This is for a new mission only: to change a request that still awaits a decision, call revise_pending_request with its id, so the person keeps one card to decide.",
|
|
1411
1505
|
inputSchema: {
|
|
1412
|
-
task: z.string().min(1).max(12000).describe("What the session does and what it returns."),
|
|
1506
|
+
task: z.string().min(1).max(12000).describe("What the session does and what it returns. State the result that will exist rather than the mechanism, unless the person asked for the mechanism."),
|
|
1413
1507
|
circle,
|
|
1414
1508
|
refs,
|
|
1415
1509
|
primary_issue: primaryIssue,
|
|
1510
|
+
delivery: deliveryField,
|
|
1511
|
+
person_words: personWordsField,
|
|
1416
1512
|
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."),
|
|
1417
1513
|
cwd: z.string().max(300).optional().describe("Working folder for a new session (default: the factory runner's defaultCwd on the host)."),
|
|
1418
1514
|
provider: z
|
|
@@ -1455,12 +1551,15 @@ export function registerTools(server, ctx) {
|
|
|
1455
1551
|
provider: a.provider,
|
|
1456
1552
|
resolved_provider: resolution.provider.id,
|
|
1457
1553
|
...(a.primary_issue ? { primary_issue: a.primary_issue } : {}),
|
|
1554
|
+
...(a.delivery ? { delivery: a.delivery } : {}),
|
|
1555
|
+
...(a.person_words ? { person_words: a.person_words } : {}),
|
|
1458
1556
|
...(a.steering ? { steering: a.steering } : {}),
|
|
1459
1557
|
},
|
|
1460
1558
|
});
|
|
1461
1559
|
const authorization = providerAuthorizationOf(all().find((x) => x.id === r.id) ?? r);
|
|
1462
1560
|
return {
|
|
1463
1561
|
...r,
|
|
1562
|
+
draft_review: await draftReviewOf(r),
|
|
1464
1563
|
provider: {
|
|
1465
1564
|
requested: a.provider ?? null,
|
|
1466
1565
|
resolved: resolution.provider.id,
|
|
@@ -1506,6 +1605,8 @@ export function registerTools(server, ctx) {
|
|
|
1506
1605
|
circle: circle.optional(),
|
|
1507
1606
|
refs: z.array(z.string()).max(50).optional().describe("The whole new list of refs, replacing the old one."),
|
|
1508
1607
|
primary_issue: primaryIssue,
|
|
1608
|
+
delivery: deliveryField,
|
|
1609
|
+
person_words: personWordsField,
|
|
1509
1610
|
session: z.string().regex(/^[A-Za-z0-9._-]{3,80}$/).optional(),
|
|
1510
1611
|
cwd: z.string().max(300).optional(),
|
|
1511
1612
|
provider: z.enum(["codex", "pi", "copilot", "claude"]).optional().describe("Verified installed on the connector host again, as request_authorized_run does."),
|
|
@@ -1554,6 +1655,9 @@ export function registerTools(server, ctx) {
|
|
|
1554
1655
|
}
|
|
1555
1656
|
if (c.primary_issue !== undefined)
|
|
1556
1657
|
body.primary_issue = c.primary_issue;
|
|
1658
|
+
for (const k of ["delivery", "person_words"])
|
|
1659
|
+
if (c[k] !== undefined)
|
|
1660
|
+
body[k] = c[k];
|
|
1557
1661
|
const nextRefs = withPrimary(c.refs ?? live?.refs ?? [], c.primary_issue ?? body.primary_issue);
|
|
1558
1662
|
let record;
|
|
1559
1663
|
try {
|
|
@@ -1578,6 +1682,7 @@ export function registerTools(server, ctx) {
|
|
|
1578
1682
|
const revisedAuthorization = providerAuthorizationOf(all().find((x) => x.id === (record.supersedes ?? record.id)));
|
|
1579
1683
|
return {
|
|
1580
1684
|
...revisionOut(record.supersedes ? { ...record, id: record.supersedes } : record),
|
|
1685
|
+
draft_review: await draftReviewOf(all().find((x) => x.id === (record.supersedes ?? record.id)) ?? record),
|
|
1581
1686
|
revised: record.revision,
|
|
1582
1687
|
...(provider ? { provider } : {}),
|
|
1583
1688
|
...(revisedAuthorization ? { provider_authorization: authorizationOut(revisedAuthorization) } : {}),
|
|
@@ -1656,10 +1761,11 @@ export function registerTools(server, ctx) {
|
|
|
1656
1761
|
}));
|
|
1657
1762
|
server.registerTool("request_session_steer", {
|
|
1658
1763
|
title: "Send one authorized follow-up to a running agent",
|
|
1659
|
-
description: "
|
|
1764
|
+
description: "Send one correction to a running agent within an approved run's steering grant (jgwill/Miadi#893, #930). Busy is normal: send it while the agent works, once, and do not watch its tools, wait for idle or retry. The connector hands it to the bound session's own inbox, and the agent's runtime presents it to its model at its next continuation in the same turn. The answer's delivery says which state it reached, each with its source and time: forwarded_to_runtime and queued_at_runtime do not show the model read it; delivered_to_agent_context, acknowledged and behavior_adjusted are read from the session's transcript. A provider with no adapter, or a session started before the adapter, answers unsupported with the reason and uses no attempt. An idle seat has no continuation coming, so its steer is typed into the mission's bound pane, never whichever pane is active. When the run's grant does not cover this, the steer waits for one explicit scoped human decision on the returned approval_url. Read it later with get_run_result on the steer's id or with mission_status.",
|
|
1660
1765
|
inputSchema: {
|
|
1661
1766
|
run_request: z.string().min(1).describe("The approved run_request or work_proposal whose steering grant covers this steer."),
|
|
1662
|
-
instruction: z.string().min(1).max(4000).describe("The exact instruction
|
|
1767
|
+
instruction: z.string().min(1).max(4000).describe("The exact instruction for the agent. It is material within the approved mission's scope, not new authority."),
|
|
1768
|
+
expect_next_action: z.string().min(1).max(200).optional().describe("Words to look for in the agent's next tool inputs. When they appear after delivery, the state becomes behavior_adjusted; without this, whether the next actions follow the correction is a person's judgment."),
|
|
1663
1769
|
session: z.string().regex(/^[A-Za-z0-9._-]{3,80}$/).optional().describe("The session to steer; must be the one the grant binds."),
|
|
1664
1770
|
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."),
|
|
1665
1771
|
refs,
|
|
@@ -1683,15 +1789,36 @@ export function registerTools(server, ctx) {
|
|
|
1683
1789
|
}
|
|
1684
1790
|
const grant = steeringGrantOf(grantRecord);
|
|
1685
1791
|
const priorAttempts = steerAttempts(all(), grantRecord.id);
|
|
1686
|
-
const
|
|
1792
|
+
const deliveryOf = (rec) => (ctx.steeringRuntime ? readSteerDelivery(ctx.steeringRuntime, rec) ?? undefined : undefined);
|
|
1793
|
+
const wasSent = (rec) => {
|
|
1794
|
+
const receipt = rec.completion_receipt;
|
|
1795
|
+
return receipt?.typed === true || receipt?.forwarded === true;
|
|
1796
|
+
};
|
|
1797
|
+
const attemptsNow = (counted) => ({ used: steerAttempts(all(), grantRecord.id), ...(grant?.max_attempts !== undefined ? { max: grant.max_attempts } : {}), counted });
|
|
1798
|
+
// The same key after a client's retry or a dropped connection gets the first answer, whatever the grant says by now.
|
|
1799
|
+
if (a.idempotency_key) {
|
|
1800
|
+
const sameKey = all().find((x) => x.kind === "session_steer_request" && x.idempotency_key === a.idempotency_key && x.actor.person_id === person.id && x.refs.includes(grantRecord.id));
|
|
1801
|
+
if (sameKey && wasSent(sameKey))
|
|
1802
|
+
return { ...sameKey, delivery: deliveryOf(sameKey), attempts: attemptsNow(false) };
|
|
1803
|
+
if (sameKey && steersInFlight.has(sameKey.id)) {
|
|
1804
|
+
await steersInFlight.get(sameKey.id);
|
|
1805
|
+
const settled = all().find((x) => x.id === sameKey.id) ?? sameKey;
|
|
1806
|
+
return { ...settled, delivery: deliveryOf(settled), attempts: attemptsNow(false) };
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const revoked = steeringRevoked(all(), grantRecord.id);
|
|
1810
|
+
const covered = checkGrant(grant, priorAttempts, new Date(), revoked) === "ok";
|
|
1687
1811
|
// The pane the factory delivered this mission to: the steer goes there and nowhere else (jgwill/Miadi#909).
|
|
1688
1812
|
const bound = receiptsForRecord(receipts(), grantRecord.id)[0];
|
|
1689
|
-
const binding = bound
|
|
1813
|
+
const binding = bound
|
|
1814
|
+
? { session: bound.tmux_session, ...(bound.pane_id ? { pane_id: bound.pane_id } : {}), ...(bound.session_id ? { session_id: bound.session_id } : {}), ...(bound.provider ? { provider: bound.provider } : {}) }
|
|
1815
|
+
: undefined;
|
|
1690
1816
|
const body = {
|
|
1691
1817
|
instruction: a.instruction,
|
|
1692
1818
|
session: a.session ?? bound?.tmux_session ?? grantRecord.body.session,
|
|
1693
1819
|
pane: a.pane ?? grant?.pane,
|
|
1694
|
-
...(
|
|
1820
|
+
...(a.expect_next_action ? { expect_next_action: a.expect_next_action } : {}),
|
|
1821
|
+
...(binding ? { binding: { ...binding, by: "factory_receipt", delivered_at: bound.at } } : {}),
|
|
1695
1822
|
};
|
|
1696
1823
|
const fields = {
|
|
1697
1824
|
circle: grantRecord.circle,
|
|
@@ -1709,42 +1836,113 @@ export function registerTools(server, ctx) {
|
|
|
1709
1836
|
...r,
|
|
1710
1837
|
approval_url: approvalUrl(r.id),
|
|
1711
1838
|
state: "awaiting_approval",
|
|
1712
|
-
reason: !grant ? "no_grant" : checkGrant(grant, priorAttempts),
|
|
1839
|
+
reason: !grant ? "no_grant" : checkGrant(grant, priorAttempts, new Date(), revoked),
|
|
1713
1840
|
message: "The approved run's steering grant does not cover this steer; a person's decision is requested, once, for exactly this instruction.",
|
|
1714
1841
|
};
|
|
1715
1842
|
}
|
|
1716
1843
|
const r = append("session_steer_request", person, auth, { ...fields, approval_state: "not_required" });
|
|
1717
|
-
const
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1844
|
+
const sameWords = all().find((x) => x.kind === "session_steer_request" && x.id !== r.id && x.refs.includes(grantRecord.id) && x.body.instruction === a.instruction && wasSent(x));
|
|
1845
|
+
let finishSend = () => { };
|
|
1846
|
+
steersInFlight.set(r.id, new Promise((resolve) => (finishSend = resolve)));
|
|
1847
|
+
try {
|
|
1848
|
+
const result = await steerSession({ tmux: ctx.tmux, stateDir: config.stateDir, ...(ctx.steeringRuntime ? { runtime: ctx.steeringRuntime } : {}), ...(ctx.steerSettleMs !== undefined ? { settleMs: ctx.steerSettleMs } : {}) }, { record: r, grantRecord, priorAttempts, ...(binding ? { binding } : {}), ...(sameWords ? { sameInstructionSent: sameWords.id } : {}) });
|
|
1849
|
+
// A typed or forwarded steer is never "failed": failed is re-runnable, and that would send it twice (#922 R8).
|
|
1850
|
+
const delivered = result.typed || result.forwarded === true;
|
|
1851
|
+
ledger.append({
|
|
1852
|
+
id: newRecordId(r.kind),
|
|
1853
|
+
kind: r.kind,
|
|
1854
|
+
created_at: new Date().toISOString(),
|
|
1855
|
+
actor: r.actor,
|
|
1856
|
+
refs: r.refs,
|
|
1857
|
+
evidence_refs: [],
|
|
1858
|
+
supersedes: r.id,
|
|
1859
|
+
approval_state: "not_required",
|
|
1860
|
+
// Forwarded is under way: the runtime has not presented it yet, and delivery reads where it stands.
|
|
1861
|
+
state: result.forwarded ? "in_progress" : delivered ? "completed" : "blocked",
|
|
1862
|
+
completion_receipt: {
|
|
1863
|
+
decision: "covered_by_grant",
|
|
1864
|
+
decided_by: { person_id: person.id, name: person.name },
|
|
1865
|
+
decided_at: new Date().toISOString(),
|
|
1866
|
+
outcome: delivered ? result.status : "failed",
|
|
1867
|
+
steer_status: result.status,
|
|
1868
|
+
typed: result.typed,
|
|
1869
|
+
...(result.forwarded ? { forwarded: true } : {}),
|
|
1870
|
+
...(result.route ? { route: result.route } : {}),
|
|
1871
|
+
...(result.transcript_offset !== undefined ? { transcript_offset: result.transcript_offset } : {}),
|
|
1872
|
+
message: result.message,
|
|
1873
|
+
...(result.target ? { target: result.target } : {}),
|
|
1874
|
+
...(result.artifact ? { artifact: result.artifact } : {}),
|
|
1875
|
+
},
|
|
1876
|
+
body: r.body,
|
|
1877
|
+
});
|
|
1878
|
+
const stored = all().find((x) => x.id === r.id) ?? r;
|
|
1879
|
+
return {
|
|
1880
|
+
...(records().find((x) => x.id === r.id) ?? r),
|
|
1881
|
+
delivery: deliveryOf(stored),
|
|
1882
|
+
attempts: attemptsNow(delivered),
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
finally {
|
|
1886
|
+
finishSend();
|
|
1887
|
+
steersInFlight.delete(r.id);
|
|
1888
|
+
}
|
|
1889
|
+
}));
|
|
1890
|
+
server.registerTool("revoke_session_steering", {
|
|
1891
|
+
title: "Stop a run's steering, or one steer still waiting",
|
|
1892
|
+
description: "A person stops a correction before its agent reads it (jgwill/Miadi#930). Name the run to stop all further steers under its grant and everything still waiting in its session's inbox, or add steer to stop only that one. The agent's runtime checks again when it would consume it. The answer says exactly what was revoked and what the agent already consumed: a correction already read cannot be taken back.",
|
|
1893
|
+
inputSchema: {
|
|
1894
|
+
run_request: z.string().min(1).describe("The run_request or work_proposal the steering belongs to."),
|
|
1895
|
+
steer: z.string().min(1).optional().describe("One session_steer_request to stop; without it, the run's whole steering grant is revoked."),
|
|
1896
|
+
reason: z.string().min(1).max(1000),
|
|
1897
|
+
refs,
|
|
1898
|
+
...base,
|
|
1899
|
+
},
|
|
1900
|
+
annotations: WRITE,
|
|
1901
|
+
}, wrap(async (a, extra) => {
|
|
1902
|
+
const { person, auth } = who(extra);
|
|
1903
|
+
requireContribute(auth);
|
|
1904
|
+
requireTier(person, "write");
|
|
1905
|
+
const grantRecord = all().find((r) => r.id === a.run_request);
|
|
1906
|
+
if (!grantRecord || (grantRecord.kind !== "run_request" && grantRecord.kind !== "work_proposal")) {
|
|
1907
|
+
throw new ToolRefusal("invalid_ref", "Give the id of the run_request or work_proposal whose steering this stops.");
|
|
1908
|
+
}
|
|
1909
|
+
if (a.steer) {
|
|
1910
|
+
const steer = all().find((r) => r.id === a.steer);
|
|
1911
|
+
if (!steer || steer.kind !== "session_steer_request" || !steer.refs.includes(grantRecord.id))
|
|
1912
|
+
throw new ToolRefusal("invalid_ref", `${a.steer} is not a steer of ${grantRecord.id}.`);
|
|
1913
|
+
}
|
|
1914
|
+
const r = append("steering_revocation", person, auth, {
|
|
1915
|
+
circle: grantRecord.circle,
|
|
1916
|
+
refs: [grantRecord.id, ...(a.steer ? [a.steer] : []), ...a.refs],
|
|
1917
|
+
intention: a.intention,
|
|
1918
|
+
role: a.role,
|
|
1919
|
+
idempotency_key: a.idempotency_key,
|
|
1920
|
+
body: { reason: a.reason, ...(a.steer ? { steer: a.steer } : {}) },
|
|
1742
1921
|
});
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1922
|
+
// A steer still waiting for a person's decision is closed too, so approving it later cannot run it.
|
|
1923
|
+
if (a.steer) {
|
|
1924
|
+
const waiting = all().find((x) => x.id === a.steer);
|
|
1925
|
+
if (waiting?.approval_state === "awaiting_approval") {
|
|
1926
|
+
ledger.append({
|
|
1927
|
+
id: newRecordId(waiting.kind),
|
|
1928
|
+
kind: waiting.kind,
|
|
1929
|
+
created_at: new Date().toISOString(),
|
|
1930
|
+
actor: waiting.actor,
|
|
1931
|
+
refs: waiting.refs,
|
|
1932
|
+
evidence_refs: [],
|
|
1933
|
+
supersedes: waiting.id,
|
|
1934
|
+
approval_state: "declined",
|
|
1935
|
+
state: "withdrawn",
|
|
1936
|
+
completion_receipt: { decision: "revoked", decided_by: { person_id: person.id, name: person.name }, decided_at: new Date().toISOString(), outcome: "revoked", steer_status: "blocked", typed: false, message: a.reason },
|
|
1937
|
+
body: waiting.body,
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
const sessions = [...new Set(receiptsForRecord(receipts(), grantRecord.id).map((x) => x.session_id).filter((x) => Boolean(x)))];
|
|
1942
|
+
const inboxes = ctx.steeringRuntime
|
|
1943
|
+
? sessions.map((session_id) => ({ session_id, ...revokeInInbox(ctx.steeringRuntime, session_id, { grant: grantRecord.id, ...(a.steer ? { steer: a.steer } : {}) }, a.reason) }))
|
|
1944
|
+
: [];
|
|
1945
|
+
return { ...r, inboxes, note: a.steer ? "This steer was stopped where it had not been consumed." : "No further steer runs under this grant without a person's decision on that exact steer." };
|
|
1748
1946
|
}));
|
|
1749
1947
|
server.registerTool("request_review_operation", {
|
|
1750
1948
|
title: "Request a review operation",
|
|
@@ -1917,6 +2115,78 @@ export function registerTools(server, ctx) {
|
|
|
1917
2115
|
research_url: `${config.miadiPublicUrl}/research`,
|
|
1918
2116
|
};
|
|
1919
2117
|
}));
|
|
2118
|
+
server.registerTool("list_research_artifacts", {
|
|
2119
|
+
title: "List what is on the inquiry shelf",
|
|
2120
|
+
description: "Reports and folders on Miadi's private inquiry shelf, as you may read them: each one's title, opening, words, sections, sources, files, date, owner, visibility, issue, episodes and, for a saved report, its sha256. All of it is computed from the files, never summarised. scope mine lists what you own; scope all, for Miadi's write tier, lists every folder. Search with query, narrow with kind, order with sort, and page with offset and next_offset. Read one with read_research_artifact. Read only: nothing is changed.",
|
|
2121
|
+
inputSchema: {
|
|
2122
|
+
scope: z.enum(["mine", "all"]).default("mine"),
|
|
2123
|
+
query: z.string().max(200).optional().describe("Words to find in a title, an opening, a heading or a folder's name."),
|
|
2124
|
+
kind: z.enum(["report", "folder", "notes"]).optional().describe("report: saved through a door. folder: holds a document. notes: agent instructions or unreadable files only."),
|
|
2125
|
+
sort: z.enum(["newest", "oldest", "longest", "title"]).default("newest"),
|
|
2126
|
+
limit: z.number().int().min(1).max(200).default(50),
|
|
2127
|
+
offset: z.number().int().min(0).default(0).describe("0, or next_offset from the previous page."),
|
|
2128
|
+
},
|
|
2129
|
+
annotations: READ,
|
|
2130
|
+
}, wrap(async (a, extra) => {
|
|
2131
|
+
const { person } = who(extra);
|
|
2132
|
+
if (!config.inquiryRoot)
|
|
2133
|
+
throw new ToolRefusal("unavailable", "This connector host has no inquiry shelf configured.");
|
|
2134
|
+
try {
|
|
2135
|
+
const { scope, query, kind, sort, limit, offset } = a;
|
|
2136
|
+
return await listShelfArtefacts(config.inquiryRoot, person, {
|
|
2137
|
+
...(scope ? { scope } : {}),
|
|
2138
|
+
...(query ? { query } : {}),
|
|
2139
|
+
...(kind ? { kind } : {}),
|
|
2140
|
+
...(sort ? { sort } : {}),
|
|
2141
|
+
...(limit !== undefined ? { limit } : {}),
|
|
2142
|
+
...(offset !== undefined ? { offset } : {}),
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
catch (error) {
|
|
2146
|
+
if (error instanceof ShelfRefusal)
|
|
2147
|
+
throw new ToolRefusal(error.code, error.message);
|
|
2148
|
+
throw error;
|
|
2149
|
+
}
|
|
2150
|
+
}));
|
|
2151
|
+
server.registerTool("read_research_artifact", {
|
|
2152
|
+
title: "Read a document on the inquiry shelf",
|
|
2153
|
+
description: "Read back what is on Miadi's private inquiry shelf, as you: a folder's owner, visibility, its issue, its episodes, the commit that holds it, whether anything on disk differs from that commit, and every file with its bytes and sha256. With the folder's report, or the file you name, you also get its text, in pages when long: pass page.next back; joined, the pages hash to sha256. integrity says whether a saved report's bytes are still the bytes saved. Only the folder's owner or a person with Miadi's write tier can read it. Text files only; hidden files and links are never served. Read only: nothing is changed.",
|
|
2154
|
+
inputSchema: {
|
|
2155
|
+
artefact: z.string().min(1).max(300).describe("The shelf folder's name, inquiry-artefact:<name> or the research_artifact record id save_research returned."),
|
|
2156
|
+
file: z.string().min(1).max(500).optional().describe("A path in the folder, as files lists it. Default: its saved report, if it has one."),
|
|
2157
|
+
offset: z.number().int().min(0).optional().describe("0, or page.next.offset from the previous page."),
|
|
2158
|
+
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."),
|
|
2159
|
+
max_bytes: z.number().int().min(MIN_PAGE_BYTES).max(MAX_TEXT_BYTES).optional().describe(`Page size in bytes (default ${DEFAULT_PAGE_BYTES}).`),
|
|
2160
|
+
},
|
|
2161
|
+
annotations: READ,
|
|
2162
|
+
}, wrap(async (a, extra) => {
|
|
2163
|
+
const { person } = who(extra);
|
|
2164
|
+
if (!config.inquiryRoot)
|
|
2165
|
+
throw new ToolRefusal("unavailable", "This connector host has no inquiry shelf configured.");
|
|
2166
|
+
const ref = a.artefact.trim();
|
|
2167
|
+
let name = ref.replace(/^inquiry-artefact:/, "");
|
|
2168
|
+
if (ref.startsWith("research_artifact:")) {
|
|
2169
|
+
const held = all().find((r) => r.id === ref && r.kind === "research_artifact");
|
|
2170
|
+
const artefact = held?.body?.artefact;
|
|
2171
|
+
if (typeof artefact !== "string")
|
|
2172
|
+
throw new ToolRefusal("not_found", `No research_artifact ${ref}.`);
|
|
2173
|
+
name = artefact;
|
|
2174
|
+
}
|
|
2175
|
+
try {
|
|
2176
|
+
const { file, offset, revision, max_bytes: maxBytes } = a;
|
|
2177
|
+
return await readShelfArtefact(config.inquiryRoot, name, person, {
|
|
2178
|
+
...(file !== undefined ? { file } : {}),
|
|
2179
|
+
...(offset !== undefined ? { offset } : {}),
|
|
2180
|
+
...(revision !== undefined ? { revision } : {}),
|
|
2181
|
+
...(maxBytes !== undefined ? { maxBytes } : {}),
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
catch (error) {
|
|
2185
|
+
if (error instanceof ShelfRefusal)
|
|
2186
|
+
throw new ToolRefusal(error.code, error.message);
|
|
2187
|
+
throw error;
|
|
2188
|
+
}
|
|
2189
|
+
}));
|
|
1920
2190
|
server.registerTool("request_inquiry_episode", {
|
|
1921
2191
|
title: "Request that an inquiry be woven into an episode, or start one",
|
|
1922
2192
|
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.",
|
|
@@ -2022,6 +2292,13 @@ export function registerTools(server, ctx) {
|
|
|
2022
2292
|
intention: r.intention,
|
|
2023
2293
|
consequence: consequenceOf(r, providerAuthorizationOf(r)),
|
|
2024
2294
|
detail: detailOf(r),
|
|
2295
|
+
...(r.kind === "run_request" || r.kind === "work_proposal"
|
|
2296
|
+
? {
|
|
2297
|
+
delivery: r.body.delivery,
|
|
2298
|
+
person_words: r.body.person_words,
|
|
2299
|
+
draft_review: ctx.draftReview?.stored(r) ?? null,
|
|
2300
|
+
}
|
|
2301
|
+
: {}),
|
|
2025
2302
|
...(r.kind === "run_request" ? { provider_authorization: authorizationOut(providerAuthorizationOf(r)) } : {}),
|
|
2026
2303
|
approval_url: approvalUrl(r.id),
|
|
2027
2304
|
projection: requestProjection(r, { reviewBase: ctx.approval?.config.reviewUrl, factoryDir: ctx.factoryDir }),
|
|
@@ -2227,20 +2504,23 @@ export const TOOL_STATUS = {
|
|
|
2227
2504
|
register_source_intent: "live, contribute",
|
|
2228
2505
|
record_handoff: "live, contribute",
|
|
2229
2506
|
update_handoff: "live, contribute",
|
|
2230
|
-
propose_work: "live, contribute, needs_approval (approving starts the work as a factory run)",
|
|
2507
|
+
propose_work: "live, contribute, needs_approval (approving starts the work as a factory run; answers with draft_review)",
|
|
2231
2508
|
prepare_calendar_event: "live, contribute (draft and .ics only)",
|
|
2232
2509
|
propose_session_completion: "live, contribute, needs_approval (decision recorded, nothing closed)",
|
|
2233
|
-
request_authorized_run: "live, contribute, needs_approval (queues a factory brief once approved; may name a verified provider and a bounded steering grant)",
|
|
2234
|
-
revise_pending_request: "live, contribute (a waiting or sent-back run or proposal gets its next version; same id, same card)",
|
|
2510
|
+
request_authorized_run: "live, contribute, needs_approval (queues a factory brief once approved; may name a verified provider and a bounded steering grant; answers with draft_review)",
|
|
2511
|
+
revise_pending_request: "live, contribute (a waiting or sent-back run or proposal gets its next version; same id, same card; answers with draft_review)",
|
|
2235
2512
|
get_request_history: "live (every version of a request, oldest first; whoever asked, or the write tier)",
|
|
2236
2513
|
supersede_pending_requests: "live, contribute (retires named earlier requests still waiting and joins them as earlier versions of the one that stands)",
|
|
2237
|
-
request_session_steer: "live, contribute, write tier (runs inside an approved run's steering grant, else one scoped human decision; get_capabilities.steering says whether the transport works now)",
|
|
2514
|
+
request_session_steer: "live, contribute, write tier (one correction to a working agent, forwarded to its runtime; runs inside an approved run's steering grant, else one scoped human decision; get_capabilities.steering says whether the transport works now and which providers have an adapter)",
|
|
2515
|
+
revoke_session_steering: "live, contribute, write tier (stops a run's steering, or one steer not yet consumed)",
|
|
2238
2516
|
request_review_operation: "live, contribute, needs_approval",
|
|
2239
2517
|
request_screenwalk_reading: "live, contribute, needs_approval (host provider adapter required; stops at editorial decision)",
|
|
2240
2518
|
request_reading_import: "live, contribute, needs_approval (exact public import; atomic service contract required)",
|
|
2241
2519
|
request_deep_diver: "live, contribute, needs_approval",
|
|
2242
2520
|
link_review_to_episode: "live, contribute, needs_approval",
|
|
2243
2521
|
save_research: "live, contribute (the whole report, in parts when long; saved on the inquiry shelf with its issue, pushed; private by default)",
|
|
2522
|
+
list_research_artifacts: "live (the shelf as you may read it: yours, or every folder for the write tier; searched, sorted, paged)",
|
|
2523
|
+
read_research_artifact: "live (a shelf folder's manifest and its text files, paged, with sha256 and the commit that holds them; its owner or Miadi's write tier)",
|
|
2244
2524
|
request_inquiry_episode: "live, contribute, needs_approval (weaves a shelf folder into an episode or starts one)",
|
|
2245
2525
|
show_decisions: "live (cards in the conversation; deciding on them needs the write tier and the miadi:approve sign-in)",
|
|
2246
2526
|
decide_request: "card only: the person's click on a decisions card; not offered to the assistant",
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.22";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.1.
|
|
1
|
+
export const VERSION = "0.1.22";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miadi/connector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
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",
|
|
@@ -16,6 +16,18 @@
|
|
|
16
16
|
"types": "./dist/records.d.ts",
|
|
17
17
|
"import": "./dist/records.js"
|
|
18
18
|
},
|
|
19
|
+
"./research-digest": {
|
|
20
|
+
"types": "./dist/research-digest.d.ts",
|
|
21
|
+
"import": "./dist/research-digest.js"
|
|
22
|
+
},
|
|
23
|
+
"./runtime-steer": {
|
|
24
|
+
"types": "./dist/runtime-steer.d.ts",
|
|
25
|
+
"import": "./dist/runtime-steer.js"
|
|
26
|
+
},
|
|
27
|
+
"./research-shelf": {
|
|
28
|
+
"types": "./dist/research-shelf.d.ts",
|
|
29
|
+
"import": "./dist/research-shelf.js"
|
|
30
|
+
},
|
|
19
31
|
"./package.json": "./package.json"
|
|
20
32
|
},
|
|
21
33
|
"bin": {
|
|
@@ -26,7 +38,7 @@
|
|
|
26
38
|
"README.md"
|
|
27
39
|
],
|
|
28
40
|
"dependencies": {
|
|
29
|
-
"@miadi/inquiry-weave": "0.18.
|
|
41
|
+
"@miadi/inquiry-weave": "0.18.10",
|
|
30
42
|
"@miadi/node-service-kit": "0.1.2",
|
|
31
43
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
32
44
|
"express": "^5.2.1",
|