@kontourai/survey 0.8.0 → 1.1.0

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.
@@ -175,7 +175,7 @@ function buildConsoleHtml(sessionPath) {
175
175
  // workbench.js lives at dist/src/review-workbench/review-workbench.js relative to distRoot
176
176
  const workbenchJsPath = "/dist/src/review-workbench/review-workbench.js";
177
177
  const workbenchCssPath = "/dist/src/review-workbench/review-workbench.css";
178
- const tokensIndexPath = "/dist/src/review-workbench/vendor/console-kit/tokens/index.css";
178
+ const tokensIndexPath = "/dist/src/review-workbench/vendor/kontourai-ui/tokens/index.css";
179
179
  return `<!doctype html>
180
180
  <html lang="en" class="theme-survey">
181
181
  <head>
@@ -293,9 +293,11 @@ body {
293
293
  <body>
294
294
  <header class="console-topbar" role="banner">
295
295
  <div class="console-topbar-logo">
296
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
297
- <rect width="14" height="14" rx="3" fill="#5ce0c6" fill-opacity="0.15"/>
298
- <path d="M3.5 7.2L5.9 9.8L10.5 4.5" stroke="#5ce0c6" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
296
+ <svg class="product-mark" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Survey product mark">
297
+ <circle cx="10" cy="10" r="5.5"/>
298
+ <path d="M14 14 19.5 19.5"/>
299
+ <path d="M7.6 9H12.4"/>
300
+ <path d="M7.6 11.2H11.2"/>
299
301
  </svg>
300
302
  Survey Review Console
301
303
  </div>
@@ -7,8 +7,6 @@ export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js"
7
7
  export type { ReviewedCandidateResolutionInput } from "./reviewed-candidate-resolution.js";
8
8
  export { reviewedCurrentProposedResolution } from "./reviewed-current-proposed-resolution.js";
9
9
  export type { CurrentProposedCandidateRole, ReviewedCurrentProposedResolutionInput, } from "./reviewed-current-proposed-resolution.js";
10
- export { flowTrustArtifactFromReviewOutcome } from "./to-flow-artifact.js";
11
- export type { FlowTrustArtifact, FlowTrustArtifactOptions } from "./to-flow-artifact.js";
12
10
  export { buildSurveyTrustBundle } from "./to-surface.js";
13
11
  export type { BuildSurveyTrustBundleOptions } from "./to-surface.js";
14
12
  export { buildSurveyLearningProjections } from "./learning-projections.js";
package/dist/src/index.js CHANGED
@@ -2,7 +2,6 @@ export { reviewResourceApiVersion } from "./review-resource.js";
2
2
  export { candidateReviewRecord, SurveyInputBuilder } from "./builder.js";
3
3
  export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js";
4
4
  export { reviewedCurrentProposedResolution } from "./reviewed-current-proposed-resolution.js";
5
- export { flowTrustArtifactFromReviewOutcome } from "./to-flow-artifact.js";
6
5
  export { buildSurveyTrustBundle } from "./to-surface.js";
7
6
  export { buildSurveyLearningProjections } from "./learning-projections.js";
8
7
  export { buildCanonicalReviewProofPayload, buildReviewProofAnchor, canonicalReviewProofJson, hashCanonicalReviewProofPayload, REVIEW_PROOF_CONTRACT_VERSION, REVIEW_PROOF_PACKAGE_NAME, REVIEW_PROOF_SCHEMA, REVIEW_PROOF_SCHEMA_VERSION, } from "./review-proof.js";
@@ -14,6 +14,17 @@ import { createServerReviewSessionRecord, deriveServerReviewSessionApplyResult,
14
14
  */
15
15
  const PROTOCOL_VERSION = "2025-06-18";
16
16
  const SESSION_NAME = "mcp-review-session";
17
+ // MCP Apps extension (SEP-1865). The review card is offered under both UI
18
+ // conventions so one server renders across hosts: the existing mcp-ui.dev
19
+ // embedded resource in tool results, AND a declared `ui://` resource that the
20
+ // official Apps hosts (ChatGPT/Claude) and Station's SEP-1865 resolver read via
21
+ // resources/read. The canonical pointer is the FLAT `_meta["ui/resourceUri"]`
22
+ // key (what registerAppTool emits); the nested `_meta.ui.resourceUri` is the
23
+ // convenience shape some hosts read — we emit both.
24
+ const UI_RESOURCE_URI_META_KEY = "ui/resourceUri";
25
+ const UI_CAPABILITY_EXTENSION = "io.modelcontextprotocol/ui";
26
+ const QUEUE_PANEL_URI = "ui://survey/review-card/queue";
27
+ const UI_RESOURCE_MIME = "text/html;profile=mcp-app";
17
28
  // MCP tool decision strings → ReviewWorkbenchDecision
18
29
  const MCP_DECISION_MAP = {
19
30
  accept: "accept-proposed",
@@ -420,6 +431,15 @@ function buildUiResource(item, snapshot, events, instance) {
420
431
  },
421
432
  };
422
433
  }
434
+ // Render the SEP-1865 declared review card: load the configured session, replay
435
+ // to current state, and render the active item's card HTML (the same HTML the
436
+ // embedded `queue` resource carries — here served via resources/read).
437
+ async function readQueuePanelHtml(options) {
438
+ const { snapshot, events } = await readSessionFile(options.sessionPath);
439
+ const current = events.length > 0 ? replayReviewSessionEvents(snapshot, events) : snapshot;
440
+ const activeItem = currentReviewItem(current);
441
+ return buildReviewCardHtml(activeItem, snapshot, events);
442
+ }
423
443
  // ---- Domain error (maps to isError:true, not a JSON-RPC error) -----------
424
444
  class DomainError extends Error {
425
445
  isDomainError = true;
@@ -446,7 +466,14 @@ async function handleLine(line, options, serverVersion) {
446
466
  id,
447
467
  result: {
448
468
  protocolVersion: PROTOCOL_VERSION,
449
- capabilities: { tools: { listChanged: false } },
469
+ capabilities: {
470
+ tools: { listChanged: false },
471
+ // Resources back the SEP-1865 ui:// review card (unless --no-ui).
472
+ ...(options.noUi ? {} : { resources: { listChanged: false } }),
473
+ ...(options.noUi
474
+ ? {}
475
+ : { extensions: { [UI_CAPABILITY_EXTENSION]: {} } }),
476
+ },
450
477
  serverInfo: { name: "survey-review-mcp", title: "Survey Review MCP", version: serverVersion },
451
478
  instructions: "Use survey_review_queue to inspect the queue, survey_review_item to drill into a single item, and survey_review_decide to record a decision. Decisions are persisted to the session file and are irreversible within this session.",
452
479
  },
@@ -466,6 +493,15 @@ async function handleLine(line, options, serverVersion) {
466
493
  title: "Review queue",
467
494
  description: "Return a text summary and JSON of the current review queue: all items with their status (pending, in-review, resolved, rejected, escalated), the active item, resolved/total counts, and session summary totals.",
468
495
  inputSchema: { type: "object", properties: {} },
496
+ // SEP-1865 UI pointer (both flat canonical + nested), unless --no-ui.
497
+ ...(options.noUi
498
+ ? {}
499
+ : {
500
+ _meta: {
501
+ [UI_RESOURCE_URI_META_KEY]: QUEUE_PANEL_URI,
502
+ ui: { resourceUri: QUEUE_PANEL_URI, visibility: ["model", "app"] },
503
+ },
504
+ }),
469
505
  },
470
506
  {
471
507
  name: "survey_review_item",
@@ -501,6 +537,37 @@ async function handleLine(line, options, serverVersion) {
501
537
  },
502
538
  });
503
539
  }
540
+ else if (method === "resources/list") {
541
+ send({
542
+ jsonrpc: "2.0",
543
+ id,
544
+ result: {
545
+ resources: options.noUi
546
+ ? []
547
+ : [
548
+ {
549
+ uri: QUEUE_PANEL_URI,
550
+ name: "Survey review workbench",
551
+ description: "Interactive review card for the active item in the configured review session (MCP Apps UI resource).",
552
+ mimeType: UI_RESOURCE_MIME,
553
+ },
554
+ ],
555
+ },
556
+ });
557
+ }
558
+ else if (method === "resources/read") {
559
+ const uri = typeof params?.uri === "string" ? params.uri : "";
560
+ if (options.noUi || uri !== QUEUE_PANEL_URI) {
561
+ send({ jsonrpc: "2.0", id, error: { code: -32602, message: `Unknown resource: ${uri || "(missing uri)"}` } });
562
+ return;
563
+ }
564
+ const html = await readQueuePanelHtml(options);
565
+ send({
566
+ jsonrpc: "2.0",
567
+ id,
568
+ result: { contents: [{ uri: QUEUE_PANEL_URI, mimeType: UI_RESOURCE_MIME, text: html }] },
569
+ });
570
+ }
504
571
  else if (method === "tools/call") {
505
572
  const name = typeof params?.name === "string" ? params.name : "";
506
573
  const toolArgs = (params?.arguments ?? {});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontourai/survey",
3
- "version": "0.8.0",
3
+ "version": "1.1.0",
4
4
  "description": "Producer-side source, extraction, candidate, and review contracts for projecting verified claims into Surface.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -80,14 +80,14 @@
80
80
  },
81
81
  "devDependencies": {
82
82
  "@anthropic-ai/sdk": "^0.54.0",
83
- "@kontourai/console-kit": "^0.1.0",
83
+ "@kontourai/ui": "^1.1.0",
84
84
  "@playwright/test": "^1.60.0",
85
85
  "@types/node": "^25.6.0",
86
86
  "marked": "^16.4.2",
87
87
  "typescript": "^5.8.0"
88
88
  },
89
89
  "engines": {
90
- "node": ">=20"
90
+ "node": ">=22"
91
91
  },
92
92
  "bin": {
93
93
  "survey-review-mcp": "./bin/survey-review-mcp.mjs",
@@ -1,41 +0,0 @@
1
- import type { ReviewOutcome, ReviewStatus } from "./types.js";
2
- /**
3
- * The neutral trust-artifact shape Kontour Flow consumes through
4
- * `flow attach-evidence --trust-artifact`. Flow evaluates only these fields
5
- * plus its own definition and project config; Survey stays the producer-side
6
- * authority for what the review actually decided.
7
- */
8
- export interface FlowTrustArtifact {
9
- schema_version: "0.1";
10
- artifact_type: "trust-report";
11
- subject: string;
12
- producer: string;
13
- status: string;
14
- issued_at: string;
15
- authority_traces: string[];
16
- claims: Array<{
17
- type: string;
18
- subject: string;
19
- status: string;
20
- }>;
21
- }
22
- export interface FlowTrustArtifactOptions {
23
- /** Flow claim type the gate expects, e.g. "adversarial.review". */
24
- claimType: string;
25
- /** Flow claim subject the gate expects, e.g. "adversarial-pass.review". */
26
- subject: string;
27
- /** Producer identity recorded on the artifact, e.g. "survey/adversarial-workbench". */
28
- producer: string;
29
- /** Defaults to the review outcome's reviewedAt, then the current time. */
30
- issuedAt?: string;
31
- /** Defaults to ["survey:review-outcome/<outcome id>"]. */
32
- authorityTraces?: string[];
33
- /** Override the ReviewStatus -> artifact status projection per entry. */
34
- statusMap?: Partial<Record<ReviewStatus, string>>;
35
- }
36
- /**
37
- * Projects a Survey ReviewOutcome into the neutral trust artifact Flow
38
- * consumes, so a per-round review (including an adversarial pass) can satisfy
39
- * or fail a Flow gate without Flow learning Survey vocabulary.
40
- */
41
- export declare function flowTrustArtifactFromReviewOutcome(outcome: ReviewOutcome, options: FlowTrustArtifactOptions): FlowTrustArtifact;
@@ -1,31 +0,0 @@
1
- const defaultStatusMap = {
2
- verified: "trusted",
3
- assumed: "assumed",
4
- proposed: "proposed",
5
- rejected: "rejected",
6
- };
7
- /**
8
- * Projects a Survey ReviewOutcome into the neutral trust artifact Flow
9
- * consumes, so a per-round review (including an adversarial pass) can satisfy
10
- * or fail a Flow gate without Flow learning Survey vocabulary.
11
- */
12
- export function flowTrustArtifactFromReviewOutcome(outcome, options) {
13
- if (!outcome.id) {
14
- throw new Error("flowTrustArtifactFromReviewOutcome requires a review outcome id");
15
- }
16
- const statusMap = { ...defaultStatusMap, ...options.statusMap };
17
- const status = statusMap[outcome.status];
18
- if (!status) {
19
- throw new Error(`flowTrustArtifactFromReviewOutcome: unmapped review status '${outcome.status}'`);
20
- }
21
- return {
22
- schema_version: "0.1",
23
- artifact_type: "trust-report",
24
- subject: options.subject,
25
- producer: options.producer,
26
- status,
27
- issued_at: options.issuedAt ?? outcome.reviewedAt ?? new Date().toISOString(),
28
- authority_traces: options.authorityTraces ?? [`survey:review-outcome/${outcome.id}`],
29
- claims: [{ type: options.claimType, subject: options.subject, status }],
30
- };
31
- }