@honua/mcp-server 0.0.20-alpha.0 → 0.1.0-beta.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.
Files changed (92) hide show
  1. package/README.md +92 -15
  2. package/certification/geospatial-mcp-schemas/PROVENANCE.md +15 -7
  3. package/certification/geospatial-mcp-schemas/conformance/manifest.schema.json +6 -0
  4. package/certification/geospatial-mcp-schemas/index.json +88 -9
  5. package/certification/geospatial-mcp-schemas/tools/apply_style_preset.schema.json +13 -9
  6. package/certification/geospatial-mcp-schemas/tools/buffer_features.schema.json +60 -0
  7. package/certification/geospatial-mcp-schemas/tools/edit_features.schema.json +74 -0
  8. package/certification/geospatial-mcp-schemas/tools/export_dataset.schema.json +60 -0
  9. package/certification/geospatial-mcp-schemas/tools/geocode_addresses.schema.json +34 -0
  10. package/certification/geospatial-mcp-schemas/tools/get_style.schema.json +13 -3
  11. package/certification/geospatial-mcp-schemas/tools/ingest_dataset.schema.json +45 -0
  12. package/certification/geospatial-mcp-schemas/tools/join_features.schema.json +73 -0
  13. package/certification/geospatial-mcp-schemas/tools/overlay_features.schema.json +53 -0
  14. package/certification/geospatial-mcp-schemas/tools/query_features.schema.json +20 -0
  15. package/certification/geospatial-mcp-schemas/tools/render_map.schema.json +5 -0
  16. package/certification/geospatial-mcp-schemas/tools/reproject_features.schema.json +49 -0
  17. package/certification/geospatial-mcp-schemas/tools/summarize_statistics.schema.json +71 -0
  18. package/dist/src/capability.d.ts +48 -0
  19. package/dist/src/capability.d.ts.map +1 -0
  20. package/dist/src/capability.js +44 -0
  21. package/dist/src/certification/census-data.d.ts +33 -0
  22. package/dist/src/certification/census-data.d.ts.map +1 -0
  23. package/dist/src/certification/census-data.js +506 -0
  24. package/dist/src/certification/census-fixture-client.d.ts +7 -0
  25. package/dist/src/certification/census-fixture-client.d.ts.map +1 -0
  26. package/dist/src/certification/census-fixture-client.js +226 -0
  27. package/dist/src/certification/certifier.d.ts +7 -1
  28. package/dist/src/certification/certifier.d.ts.map +1 -1
  29. package/dist/src/certification/certifier.js +52 -7
  30. package/dist/src/certification/cli.js +19 -4
  31. package/dist/src/certification/contracts.d.ts +41 -0
  32. package/dist/src/certification/contracts.d.ts.map +1 -1
  33. package/dist/src/certification/contracts.js +71 -0
  34. package/dist/src/certification/fixtures.d.ts +13 -0
  35. package/dist/src/certification/fixtures.d.ts.map +1 -1
  36. package/dist/src/certification/fixtures.js +28 -0
  37. package/dist/src/certification/json-schema.d.ts +23 -1
  38. package/dist/src/certification/json-schema.d.ts.map +1 -1
  39. package/dist/src/certification/json-schema.js +10 -2
  40. package/dist/src/certification/lifecycle.d.ts +14 -0
  41. package/dist/src/certification/lifecycle.d.ts.map +1 -0
  42. package/dist/src/certification/lifecycle.js +326 -0
  43. package/dist/src/certification/operator-catalog.d.ts +8 -0
  44. package/dist/src/certification/operator-catalog.d.ts.map +1 -1
  45. package/dist/src/certification/operator-catalog.js +189 -5
  46. package/dist/src/certification/report.d.ts.map +1 -1
  47. package/dist/src/certification/report.js +13 -0
  48. package/dist/src/certification/run.d.ts.map +1 -1
  49. package/dist/src/certification/run.js +1 -0
  50. package/dist/src/certification/schema-index.d.ts +25 -0
  51. package/dist/src/certification/schema-index.d.ts.map +1 -1
  52. package/dist/src/certification/schema-index.js +27 -0
  53. package/dist/src/certification/target.d.ts +9 -1
  54. package/dist/src/certification/target.d.ts.map +1 -1
  55. package/dist/src/certification/target.js +54 -2
  56. package/dist/src/eval/cli.js +4 -2
  57. package/dist/src/eval/corpus.d.ts +7 -0
  58. package/dist/src/eval/corpus.d.ts.map +1 -1
  59. package/dist/src/eval/corpus.js +16 -0
  60. package/dist/src/eval/drivers/deterministic.d.ts.map +1 -1
  61. package/dist/src/eval/drivers/deterministic.js +13 -0
  62. package/dist/src/eval/grade.d.ts.map +1 -1
  63. package/dist/src/eval/grade.js +20 -1
  64. package/dist/src/eval/report.d.ts +5 -0
  65. package/dist/src/eval/report.d.ts.map +1 -1
  66. package/dist/src/eval/report.js +3 -0
  67. package/dist/src/eval/runner.d.ts +6 -0
  68. package/dist/src/eval/runner.d.ts.map +1 -1
  69. package/dist/src/eval/runner.js +21 -7
  70. package/dist/src/eval/standalone-corpus.d.ts +3 -0
  71. package/dist/src/eval/standalone-corpus.d.ts.map +1 -0
  72. package/dist/src/eval/standalone-corpus.js +699 -0
  73. package/dist/src/eval/types.d.ts +25 -0
  74. package/dist/src/eval/types.d.ts.map +1 -1
  75. package/dist/src/index.d.ts +29 -5
  76. package/dist/src/index.d.ts.map +1 -1
  77. package/dist/src/index.js +41 -21
  78. package/dist/src/provenance.d.ts +44 -0
  79. package/dist/src/provenance.d.ts.map +1 -0
  80. package/dist/src/provenance.js +37 -0
  81. package/dist/src/resources/styles.d.ts.map +1 -1
  82. package/dist/src/resources/styles.js +36 -3
  83. package/dist/src/styles.d.ts +2 -0
  84. package/dist/src/styles.d.ts.map +1 -1
  85. package/dist/src/styles.js +29 -3
  86. package/dist/src/tools/apply-style-preset.d.ts.map +1 -1
  87. package/dist/src/tools/apply-style-preset.js +14 -2
  88. package/dist/src/tools/get-style.d.ts.map +1 -1
  89. package/dist/src/tools/get-style.js +24 -13
  90. package/dist/src/tools/list-services.d.ts.map +1 -1
  91. package/dist/src/tools/list-services.js +16 -1
  92. package/package.json +11 -2
@@ -286,8 +286,46 @@ export function buildOperatorTools() {
286
286
  readOnly: false,
287
287
  sampleOutput: { appPackageId: "app-001", uri: "honua://apps/app-001" },
288
288
  },
289
+ {
290
+ // OPTIONAL mutation-profile tool (geospatial-mcp `mutation` profile) — matched
291
+ // as an advertised-only known gap, never round-tripped by the read-only path;
292
+ // the mutating-round-trip contract drives its insert→update→delete lifecycle.
293
+ // This fixture opts into the optional mutation profile so that path stays
294
+ // certified; the real honua-server surface does NOT advertise this tool —
295
+ // Honua does not support AI operational data editing (honua-server ADR-0028).
296
+ name: "honua_edit_features",
297
+ title: "Edit features",
298
+ description: "Apply feature inserts, updates, and deletes to a published editable layer.",
299
+ inputSchema: OBJECT_SCHEMA({
300
+ serviceId: { type: "string", minLength: 1 },
301
+ layerId: { type: "integer", minimum: 0 },
302
+ adds: {
303
+ type: "array",
304
+ items: OBJECT_SCHEMA({ attributes: { type: "object" } }, ["attributes"]),
305
+ },
306
+ updates: {
307
+ type: "array",
308
+ items: OBJECT_SCHEMA({ objectId: { type: "integer" }, attributes: { type: "object" } }, [
309
+ "objectId",
310
+ "attributes",
311
+ ]),
312
+ },
313
+ deletes: { type: "array", items: { type: "integer" } },
314
+ }, ["serviceId", "layerId"]),
315
+ outputSchema: OBJECT_SCHEMA({
316
+ addResults: { type: "array", items: EDIT_RESULT_SCHEMA },
317
+ updateResults: { type: "array", items: EDIT_RESULT_SCHEMA },
318
+ deleteResults: { type: "array", items: EDIT_RESULT_SCHEMA },
319
+ }),
320
+ readOnly: false,
321
+ sampleOutput: { addResults: [], updateResults: [], deleteResults: [] },
322
+ },
289
323
  ];
290
324
  }
325
+ const EDIT_RESULT_SCHEMA = OBJECT_SCHEMA({ objectId: { type: "integer" }, success: { type: "boolean" } }, [
326
+ "objectId",
327
+ "success",
328
+ ]);
291
329
  export function buildOperatorResources() {
292
330
  return [
293
331
  {
@@ -347,6 +385,88 @@ function validateToolArgs(tool, args) {
347
385
  fieldPath: e.instancePath || undefined,
348
386
  }));
349
387
  }
388
+ /** Feature-store key seeded so the pagination + mutation contracts have real rows. */
389
+ const SEED_SERVICE = "svc-parks";
390
+ const SEED_LAYER = 0;
391
+ function seedFeatureStore() {
392
+ const layer = new Map();
393
+ // OBJECTID 1 first so the existing single-feature round-trip fixture is stable.
394
+ const names = ["Central Park", "Riverside Park", "Lincoln Park", "Golden Gate Park", "Prospect Park"];
395
+ names.forEach((name, i) => layer.set(i + 1, { OBJECTID: i + 1, NAME: name }));
396
+ return new Map([[`${SEED_SERVICE}::${SEED_LAYER}`, layer]]);
397
+ }
398
+ function newCatalogState() {
399
+ return { features: seedFeatureStore(), nextObjectId: 1000, jobs: new Map(), nextJobSeq: 1 };
400
+ }
401
+ /** Number of polls before the job lifecycle mock reports `Succeeded`. */
402
+ const JOB_SUCCESS_AFTER_POLLS = 3;
403
+ /** Stateful tool handlers keyed by tool name; absent ⇒ echo the tool's sampleOutput. */
404
+ function buildToolHandlers(state) {
405
+ const handlers = new Map();
406
+ handlers.set("honua_query_features", (args) => {
407
+ const key = `${String(args.serviceId)}::${Number(args.layerId)}`;
408
+ const layer = state.features.get(key) ?? new Map();
409
+ let rows = [...layer.values()].sort((a, b) => Number(a.OBJECTID) - Number(b.OBJECTID));
410
+ if (Array.isArray(args.objectIds)) {
411
+ const wanted = new Set(args.objectIds.map((v) => Number(v)));
412
+ rows = rows.filter((r) => wanted.has(Number(r.OBJECTID)));
413
+ }
414
+ const total = rows.length;
415
+ const offset = decodeFeatureOffset(args);
416
+ const limit = clampLimit(args.limit);
417
+ const page = rows.slice(offset, offset + limit);
418
+ const nextOffset = offset + page.length;
419
+ const out = {
420
+ features: page.map((attributes) => ({ attributes })),
421
+ count: page.length,
422
+ totalCount: total,
423
+ resultOffset: offset,
424
+ };
425
+ if (nextOffset < total) {
426
+ out.nextCursor = encodeCursor(nextOffset);
427
+ out.exceededTransferLimit = true;
428
+ }
429
+ else {
430
+ out.exceededTransferLimit = false;
431
+ }
432
+ return out;
433
+ });
434
+ handlers.set("honua_edit_features", (args) => {
435
+ const key = `${String(args.serviceId)}::${Number(args.layerId)}`;
436
+ let layer = state.features.get(key);
437
+ if (!layer) {
438
+ layer = new Map();
439
+ state.features.set(key, layer);
440
+ }
441
+ const addResults = asArray(args.adds).map((add) => {
442
+ const objectId = state.nextObjectId++;
443
+ layer.set(objectId, { ...(add.attributes ?? {}), OBJECTID: objectId });
444
+ return { objectId, success: true };
445
+ });
446
+ const updateResults = asArray(args.updates).map((u) => {
447
+ const objectId = Number(u.objectId);
448
+ const existing = layer.get(objectId);
449
+ if (!existing) {
450
+ return { objectId, success: false };
451
+ }
452
+ layer.set(objectId, { ...existing, ...(u.attributes ?? {}), OBJECTID: objectId });
453
+ return { objectId, success: true };
454
+ });
455
+ const deleteResults = asArray(args.deletes).map((raw) => {
456
+ const objectId = Number(raw);
457
+ return { objectId, success: layer.delete(objectId) };
458
+ });
459
+ return { addResults, updateResults, deleteResults };
460
+ });
461
+ handlers.set("honua_execute_plan", () => {
462
+ const seq = state.nextJobSeq++;
463
+ const jobId = `job-${String(seq).padStart(3, "0")}`;
464
+ const resultPackageId = `res-${jobId}`;
465
+ state.jobs.set(jobId, { jobId, polls: 0, resultPackageId });
466
+ return { jobId, status: "Submitted" };
467
+ });
468
+ return handlers;
469
+ }
350
470
  /**
351
471
  * Build the low-level MCP `Server` for the mock operator catalog. Auth is
352
472
  * enforced per request from `extra.authInfo` (populated by the HTTP transport
@@ -358,6 +478,8 @@ export function createOperatorCatalogServer(options = {}) {
358
478
  const tools = buildOperatorTools();
359
479
  const resources = buildOperatorResources();
360
480
  const toolsByName = new Map(tools.map((t) => [t.name, t]));
481
+ const state = newCatalogState();
482
+ const handlers = buildToolHandlers(state);
361
483
  const server = new Server({ name: "honua", version: "operator-mcp-mock" }, {
362
484
  capabilities: { tools: { listChanged: true }, resources: { listChanged: true } },
363
485
  instructions: "Mock honua /mcp operator surface for offline certification.",
@@ -391,9 +513,11 @@ export function createOperatorCatalogServer(options = {}) {
391
513
  if (violations.length > 0) {
392
514
  return toolErrorResult(invalidArgumentError(violations));
393
515
  }
516
+ const handler = handlers.get(tool.name);
517
+ const output = handler ? handler(request.params.arguments ?? {}) : tool.sampleOutput;
394
518
  return {
395
- content: [{ type: "text", text: JSON.stringify(tool.sampleOutput) }],
396
- structuredContent: tool.sampleOutput,
519
+ content: [{ type: "text", text: JSON.stringify(output) }],
520
+ structuredContent: output,
397
521
  isError: false,
398
522
  };
399
523
  });
@@ -408,11 +532,27 @@ export function createOperatorCatalogServer(options = {}) {
408
532
  if (!extra.authInfo) {
409
533
  throw new McpError(ErrorCode.InvalidRequest, UNAUTHENTICATED_ERROR.error.message, UNAUTHENTICATED_ERROR);
410
534
  }
411
- const resource = resources.find((r) => r.uri === request.params.uri);
535
+ const uri = request.params.uri;
536
+ // Dynamic job lifecycle resource: honua://jobs/{id} advances a documented
537
+ // Submitted→Running→Succeeded state machine on each poll.
538
+ const jobMatch = /^honua:\/\/jobs\/([^/]+)$/.exec(uri);
539
+ if (jobMatch) {
540
+ return readJobResource(state, jobMatch[1], uri);
541
+ }
542
+ // Dynamic result package (produced by a Succeeded job) or the seeded packages.
543
+ const resultMatch = /^honua:\/\/results\/([^/]+)$/.exec(uri);
544
+ if (resultMatch && !resources.some((r) => r.uri === uri)) {
545
+ return jsonResource(uri, {
546
+ resultPackageId: resultMatch[1],
547
+ status: "Ready",
548
+ artifacts: [{ kind: "FeatureService", uri: `honua://services/svc-pub-${resultMatch[1]}` }],
549
+ });
550
+ }
551
+ const resource = resources.find((r) => r.uri === uri);
412
552
  if (!resource) {
413
- throw new McpError(ErrorCode.InvalidParams, `Unknown resource: ${request.params.uri}`, {
553
+ throw new McpError(ErrorCode.InvalidParams, `Unknown resource: ${uri}`, {
414
554
  code: "not_found",
415
- error: { kind: "UnknownDataset", message: `Unknown resource: ${request.params.uri}` },
555
+ error: { kind: "UnknownDataset", message: `Unknown resource: ${uri}` },
416
556
  });
417
557
  }
418
558
  return {
@@ -421,6 +561,50 @@ export function createOperatorCatalogServer(options = {}) {
421
561
  });
422
562
  return server;
423
563
  }
564
+ function readJobResource(state, jobId, uri) {
565
+ const job = state.jobs.get(jobId);
566
+ if (!job) {
567
+ throw new McpError(ErrorCode.InvalidParams, `Unknown job: ${jobId}`, {
568
+ code: "not_found",
569
+ error: { kind: "UnknownProcess", message: `Unknown job: ${jobId}` },
570
+ });
571
+ }
572
+ job.polls++;
573
+ const succeeded = job.polls >= JOB_SUCCESS_AFTER_POLLS;
574
+ const status = succeeded ? "Succeeded" : "Running";
575
+ const fraction = Math.min(job.polls / JOB_SUCCESS_AFTER_POLLS, 1);
576
+ const body = {
577
+ jobId: job.jobId,
578
+ status,
579
+ progress: { fraction: Number(fraction.toFixed(4)), message: `step ${job.polls}` },
580
+ };
581
+ if (succeeded) {
582
+ body.resultPackageId = job.resultPackageId;
583
+ body.resultUri = `honua://results/${job.resultPackageId}`;
584
+ }
585
+ return jsonResource(uri, body);
586
+ }
587
+ function jsonResource(uri, body) {
588
+ return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(body) }] };
589
+ }
590
+ function asArray(value) {
591
+ return Array.isArray(value) ? value : [];
592
+ }
593
+ function clampLimit(raw) {
594
+ const n = Number(raw);
595
+ if (!Number.isFinite(n) || n < 1) {
596
+ return 100;
597
+ }
598
+ return Math.min(Math.floor(n), 1000);
599
+ }
600
+ /** Resolve a feature-page offset from an explicit `resultOffset` or a page `cursor`. */
601
+ function decodeFeatureOffset(args) {
602
+ if (typeof args.cursor === "string") {
603
+ return decodeCursor(args.cursor);
604
+ }
605
+ const off = Number(args.resultOffset);
606
+ return Number.isFinite(off) && off >= 0 ? Math.floor(off) : 0;
607
+ }
424
608
  function toolErrorResult(error) {
425
609
  return {
426
610
  content: [{ type: "text", text: error.error.message }],
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/certification/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CA6GlE"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/certification/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CA2HlE"}
@@ -14,6 +14,18 @@ export function renderMarkdown(report) {
14
14
  lines.push(`- Backend: \`${report.protocol.backend}\`${report.protocol.honuaTransport ? ` (Honua transport: \`${report.protocol.honuaTransport}\`)` : ""}`);
15
15
  lines.push(`- Standard: \`${report.standard.source}\` (index ${report.standard.indexDate}, ${report.standard.dialect})`);
16
16
  lines.push("");
17
+ const p = report.provenance;
18
+ lines.push("## Provenance");
19
+ lines.push("");
20
+ lines.push("| Field | Value |");
21
+ lines.push("| --- | --- |");
22
+ lines.push(`| Target | ${p.targetUrl} |`);
23
+ lines.push(`| Auth mode | \`${p.authMode}\` |`);
24
+ lines.push(`| Negotiated protocol | \`${p.protocolVersion ?? "n/a"}\` |`);
25
+ lines.push(`| Tools advertised | ${p.toolCount} |`);
26
+ lines.push(`| Suite git SHA | \`${p.suiteGitSha}\` (${p.suiteGitShaSource}) |`);
27
+ lines.push(`| Generated | \`${report.generatedAt}\` |`);
28
+ lines.push("");
17
29
  lines.push("## Summary");
18
30
  lines.push("");
19
31
  lines.push("| Metric | Value |");
@@ -27,6 +39,7 @@ export function renderMarkdown(report) {
27
39
  lines.push(`| Resources discovered | ${s.resourcesDiscovered} |`);
28
40
  lines.push(`| Prompts discovered | ${s.promptsDiscovered} |`);
29
41
  lines.push(`| Contracts checked | ${s.contractsPassed} / ${s.contractsChecked} passed |`);
42
+ lines.push(`| Contracts skipped | ${s.contractsSkipped} |`);
30
43
  lines.push(`| Known gaps | ${s.knownGaps} |`);
31
44
  lines.push(`| Failures | ${s.failures} |`);
32
45
  lines.push("");
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/certification/run.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,gBAAgB,CAAC;AAEnE,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,aAAa,CAAC;AAEhF;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAI7F;AAED,8DAA8D;AAC9D,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,EAC3B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAc9B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAGtF"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/certification/run.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,gBAAgB,CAAC;AAEnE,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,aAAa,CAAC;AAEhF;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAI7F;AAED,8DAA8D;AAC9D,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,EAC3B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAe9B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAGtF"}
@@ -16,6 +16,7 @@ export async function certifyTarget(target, env = process.env) {
16
16
  backend: target.backend,
17
17
  surface: target.serverLabel,
18
18
  honuaTransport: target.honuaTransport,
19
+ authMode: target.authMode,
19
20
  connectUnauthenticated: target.supportsUnauthenticatedPass ? () => target.connectUnauthenticated() : undefined,
20
21
  env,
21
22
  });
@@ -51,6 +51,31 @@ export interface SchemaIndex {
51
51
  * lives here in code instead.
52
52
  */
53
53
  export declare const REFERENCE_TOOL_ALIASES: Readonly<Record<string, string>>;
54
+ /**
55
+ * Platform-gated degradations (SDK-local, NOT part of the vendored standard).
56
+ *
57
+ * Some reference-shape tools require standard properties that are *platform
58
+ * bindings* — identifiers that only have meaning against a Honua
59
+ * publishing/authoring surface. The platform-free (standalone) front door
60
+ * (issue #369) has no such surface, so it intentionally degrades these tools to
61
+ * a non-error, client-side projection and legitimately omits the binding
62
+ * identifiers, keeping only the taxonomy essence.
63
+ *
64
+ * This maps an advertised reference tool name to the required standard
65
+ * properties it may shed WITHOUT counting as non-conformant on a platform-free
66
+ * target. It is deliberately narrow: it never relaxes read-addressing required
67
+ * properties (e.g. `honua_query_features` still must require serviceId+layerId),
68
+ * only true platform-authoring bindings. The vendored `index.json` and schema
69
+ * files are byte-exact copies of the upstream standard and must never be
70
+ * hand-edited to encode this, so the decision lives here in code instead.
71
+ *
72
+ * `honua_apply_style_preset`: the reference shape binds a StyleRef preset onto a
73
+ * PUBLISHED layer (serviceId+layerId) via the server OGC API – Styles authoring
74
+ * surface (ADR-0048). The platform-free surface resolves a preset for
75
+ * client-side application keyed on `styleId` alone (ADR-0028: never mutates
76
+ * server state), so it sheds serviceId+layerId.
77
+ */
78
+ export declare const PLATFORM_FREE_DEGRADATIONS: Readonly<Record<string, readonly string[]>>;
54
79
  export type JsonSchema = Record<string, unknown>;
55
80
  export declare const SCHEMA_ROOT: URL;
56
81
  /** Load and cache the machine-readable schema index. */
@@ -1 +1 @@
1
- {"version":3,"file":"schema-index.d.ts","sourceRoot":"","sources":["../../../src/certification/schema-index.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,EAAE,aAAa,GAAG,WAAW,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACnC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzD,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEnE,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAwBjD,eAAO,MAAM,WAAW,KAAsB,CAAC;AAK/C,wDAAwD;AACxD,wBAAgB,eAAe,IAAI,WAAW,CAM7C;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,CAS/D;AAED,uFAAuF;AACvF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAc3F"}
1
+ {"version":3,"file":"schema-index.d.ts","sourceRoot":"","sources":["../../../src/certification/schema-index.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,EAAE,aAAa,GAAG,WAAW,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACnC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzD,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEnE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAElF,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAwBjD,eAAO,MAAM,WAAW,KAAsB,CAAC;AAK/C,wDAAwD;AACxD,wBAAgB,eAAe,IAAI,WAAW,CAM7C;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,CAS/D;AAED,uFAAuF;AACvF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAc3F"}
@@ -12,6 +12,33 @@ import { fileURLToPath } from "node:url";
12
12
  export const REFERENCE_TOOL_ALIASES = {
13
13
  honua_dry_run_plan: "honua_validate_plan",
14
14
  };
15
+ /**
16
+ * Platform-gated degradations (SDK-local, NOT part of the vendored standard).
17
+ *
18
+ * Some reference-shape tools require standard properties that are *platform
19
+ * bindings* — identifiers that only have meaning against a Honua
20
+ * publishing/authoring surface. The platform-free (standalone) front door
21
+ * (issue #369) has no such surface, so it intentionally degrades these tools to
22
+ * a non-error, client-side projection and legitimately omits the binding
23
+ * identifiers, keeping only the taxonomy essence.
24
+ *
25
+ * This maps an advertised reference tool name to the required standard
26
+ * properties it may shed WITHOUT counting as non-conformant on a platform-free
27
+ * target. It is deliberately narrow: it never relaxes read-addressing required
28
+ * properties (e.g. `honua_query_features` still must require serviceId+layerId),
29
+ * only true platform-authoring bindings. The vendored `index.json` and schema
30
+ * files are byte-exact copies of the upstream standard and must never be
31
+ * hand-edited to encode this, so the decision lives here in code instead.
32
+ *
33
+ * `honua_apply_style_preset`: the reference shape binds a StyleRef preset onto a
34
+ * PUBLISHED layer (serviceId+layerId) via the server OGC API – Styles authoring
35
+ * surface (ADR-0048). The platform-free surface resolves a preset for
36
+ * client-side application keyed on `styleId` alone (ADR-0028: never mutates
37
+ * server state), so it sheds serviceId+layerId.
38
+ */
39
+ export const PLATFORM_FREE_DEGRADATIONS = {
40
+ honua_apply_style_preset: ["serviceId", "layerId"],
41
+ };
15
42
  /**
16
43
  * Resolved root of the vendored schema tree.
17
44
  *
@@ -1,4 +1,5 @@
1
1
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import type { AuthMode } from "../provenance.js";
2
3
  import { type ProxyOptions } from "../proxy.js";
3
4
  /**
4
5
  * Certification target selection.
@@ -14,13 +15,20 @@ import { type ProxyOptions } from "../proxy.js";
14
15
  * Reuses the proxy's upstream connection logic verbatim.
15
16
  * - `stdio-proxy` : spawns the `honua-mcp-proxy` binary against an upstream and
16
17
  * certifies through stdio, proving the proxy chain end-to-end.
18
+ * - `standalone` : the PLATFORM-FREE front door (issue #369). Certifies the
19
+ * direct-SDK `createServer` surface against an in-process fixture
20
+ * of a PLAIN public FeatureServer (recorded census layer, no
21
+ * Honua surfaces). Proves the tools certify green against a
22
+ * non-Honua endpoint, with Honua-only tools skip-with-reason.
17
23
  */
18
- export type CertTargetMode = "offline" | "remote" | "stdio-proxy";
24
+ export type CertTargetMode = "offline" | "remote" | "stdio-proxy" | "standalone";
19
25
  export interface CertificationTarget {
20
26
  mode: CertTargetMode;
21
27
  backend: "mock-upstream" | "live";
22
28
  /** Human-readable label for the certified surface (artifact provenance). */
23
29
  serverLabel: string;
30
+ /** How the authenticated client reached the surface (recorded in provenance). */
31
+ authMode: AuthMode;
24
32
  honuaTransport: string | null;
25
33
  /** Authenticated, connected MCP client. */
26
34
  client: Client;
@@ -1 +1 @@
1
- {"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../../src/certification/target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,KAAK,YAAY,EAAmB,MAAM,aAAa,CAAC;AAGjE;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,eAAe,GAAG,MAAM,CAAC;IAClC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,2BAA2B,EAAE,OAAO,CAAC;IACrC,gFAAgF;IAChF,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,cAAc,CAMtF;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,YAAY,CAc9E;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C;AAED,gDAAgD;AAChD,wBAAsB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAShH"}
1
+ {"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../../../src/certification/target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,KAAK,YAAY,EAAmB,MAAM,aAAa,CAAC;AAIjE;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC;AAEjF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,eAAe,GAAG,MAAM,CAAC;IAClC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,QAAQ,EAAE,QAAQ,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,2BAA2B,EAAE,OAAO,CAAC;IACrC,gFAAgF;IAChF,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,cAAc,CAQtF;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,YAAY,CAc9E;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C;AAED,gDAAgD;AAChD,wBAAsB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAYhH"}
@@ -2,14 +2,17 @@ import { existsSync } from "node:fs";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
4
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
6
+ import { createClientFromEnv, createServer } from "../index.js";
5
7
  import { connectUpstream } from "../proxy.js";
8
+ import { createStandaloneFixtureClient } from "./census-fixture-client.js";
6
9
  import { startMockUpstream } from "./mock-upstream.js";
7
10
  export function resolveTargetMode(env = process.env) {
8
11
  const raw = (env.HONUA_MCP_CERT_TARGET ?? "offline").trim().toLowerCase();
9
- if (raw === "offline" || raw === "remote" || raw === "stdio-proxy") {
12
+ if (raw === "offline" || raw === "remote" || raw === "stdio-proxy" || raw === "standalone") {
10
13
  return raw;
11
14
  }
12
- throw new Error(`HONUA_MCP_CERT_TARGET must be "offline", "remote", or "stdio-proxy", received "${raw}"`);
15
+ throw new Error(`HONUA_MCP_CERT_TARGET must be "offline", "remote", "stdio-proxy", or "standalone", received "${raw}"`);
13
16
  }
14
17
  /** Resolve the remote `/mcp` endpoint + credentials from the environment. */
15
18
  export function resolveRemoteProxyOptions(env) {
@@ -50,8 +53,51 @@ export async function openCertificationTarget(env = process.env) {
50
53
  if (mode === "stdio-proxy") {
51
54
  return openStdioProxyTarget(env);
52
55
  }
56
+ if (mode === "standalone") {
57
+ return openStandaloneTarget(env);
58
+ }
53
59
  return openOfflineTarget(env);
54
60
  }
61
+ /**
62
+ * Open the platform-free standalone target: the direct-SDK `createServer` surface,
63
+ * reached over an in-memory MCP transport. By default it is wired to an in-process
64
+ * fixture of a plain public FeatureServer (recorded census layer, no Honua
65
+ * surfaces) — deterministic, no network, gates CI. When `HONUA_BASE_URL` is set
66
+ * (the scheduled live variant) it drives a REAL `HonuaClient` against that public
67
+ * FeatureServer/OGC endpoint instead. Either way there is no transport auth
68
+ * boundary, so the auth contracts skip-with-reason and the Honua-only style tools
69
+ * degrade to structured "not available on this target" results.
70
+ */
71
+ async function openStandaloneTarget(env) {
72
+ const live = typeof env.HONUA_BASE_URL === "string" && env.HONUA_BASE_URL.length > 0;
73
+ const client_ = live ? createClientFromEnv(env) : createStandaloneFixtureClient();
74
+ const server = createServer(client_);
75
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
76
+ const client = new Client({ name: "honua-mcp-certifier", version: "1.0.0" });
77
+ await server.connect(serverTransport);
78
+ await client.connect(clientTransport);
79
+ return {
80
+ mode: "standalone",
81
+ backend: live ? "live" : "mock-upstream",
82
+ serverLabel: live
83
+ ? `honua-mcp standalone → ${env.HONUA_BASE_URL} (live public FeatureServer, no Honua surfaces)`
84
+ : "honua-mcp standalone → plain public FeatureServer (recorded census fixture, no Honua surfaces)",
85
+ authMode: "anonymous",
86
+ honuaTransport: env.HONUA_TRANSPORT ?? null,
87
+ client,
88
+ // A plain FeatureServer has no MCP transport auth boundary — the auth
89
+ // contracts skip-with-reason rather than assert an enforcement that a
90
+ // platform-free target does not (and should not) provide.
91
+ supportsUnauthenticatedPass: false,
92
+ connectUnauthenticated() {
93
+ return Promise.reject(new Error("standalone target has no auth boundary"));
94
+ },
95
+ async close() {
96
+ await client.close().catch(() => { });
97
+ await server.close().catch(() => { });
98
+ },
99
+ };
100
+ }
55
101
  async function openOfflineTarget(env) {
56
102
  const mock = await startMockUpstream();
57
103
  const authedClients = [];
@@ -61,6 +107,7 @@ async function openOfflineTarget(env) {
61
107
  mode: "offline",
62
108
  backend: "mock-upstream",
63
109
  serverLabel: "honua /mcp operator catalog (in-process streamable-HTTP mock)",
110
+ authMode: "bearer",
64
111
  honuaTransport: env.HONUA_TRANSPORT ?? null,
65
112
  client,
66
113
  supportsUnauthenticatedPass: true,
@@ -77,6 +124,9 @@ async function openOfflineTarget(env) {
77
124
  },
78
125
  };
79
126
  }
127
+ function resolveAuthMode(options) {
128
+ return options.authToken ? "bearer" : options.apiKey ? "api-key" : "anonymous";
129
+ }
80
130
  async function openRemoteTarget(env) {
81
131
  const options = resolveRemoteProxyOptions(env);
82
132
  const clients = [];
@@ -86,6 +136,7 @@ async function openRemoteTarget(env) {
86
136
  mode: "remote",
87
137
  backend: "live",
88
138
  serverLabel: `live honua /mcp (${options.remoteUrl})`,
139
+ authMode: resolveAuthMode(options),
89
140
  honuaTransport: env.HONUA_TRANSPORT ?? null,
90
141
  client,
91
142
  supportsUnauthenticatedPass: true,
@@ -149,6 +200,7 @@ async function openStdioProxyTarget(env) {
149
200
  mode: "stdio-proxy",
150
201
  backend,
151
202
  serverLabel: `honua-mcp-proxy → ${backend === "live" ? upstreamUrl : "in-process mock /mcp"}`,
203
+ authMode: upstreamToken ? "bearer" : env.HONUA_API_KEY ? "api-key" : "anonymous",
152
204
  honuaTransport: env.HONUA_TRANSPORT ?? null,
153
205
  client,
154
206
  supportsUnauthenticatedPass: true,
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { writeFileSync } from "node:fs";
2
+ import { mkdirSync, writeFileSync } from "node:fs";
3
3
  import { resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { resolveCorpus } from "./corpus.js";
@@ -37,7 +37,7 @@ import { runEval } from "./runner.js";
37
37
  * chain), and live remote MCP runs when HONUA_MCP_REMOTE_URL is set. Keys are
38
38
  * never hardcoded — they come from the environment.
39
39
  */
40
- const CORPUS_NAMES = ["analyst", "operator", "northstar", "all"];
40
+ const CORPUS_NAMES = ["analyst", "operator", "northstar", "standalone", "all"];
41
41
  function parseArgs(argv) {
42
42
  let artifactOnly = false;
43
43
  let offline = false;
@@ -92,7 +92,9 @@ async function main() {
92
92
  forceOffline: offline,
93
93
  ...(drivers.length > 0 ? { drivers: selectDrivers(drivers) } : {}),
94
94
  ...(corpus ? { corpus: corpusForName(corpus) } : {}),
95
+ ...(corpus === "standalone" ? { forceStandaloneSurface: true } : {}),
95
96
  });
97
+ mkdirSync(outDir, { recursive: true });
96
98
  const jsonPath = resolve(outDir, "mcp-eval-results.json");
97
99
  const mdPath = resolve(outDir, "mcp-eval-results.md");
98
100
  writeFileSync(jsonPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
@@ -24,4 +24,11 @@ export declare const CORPUS: Scenario[];
24
24
  * is set explicitly.
25
25
  */
26
26
  export declare function resolveCorpus(env?: NodeJS.ProcessEnv): Scenario[];
27
+ /**
28
+ * Whether a corpus selection targets the PLATFORM-FREE standalone surface (a plain
29
+ * public FeatureServer fixture) rather than the Honua operator/analyst surface.
30
+ * The offline runner uses this to pick the census fixture client so the standalone
31
+ * corpus resolves against real recorded non-Honua data.
32
+ */
33
+ export declare function isStandaloneCorpus(env?: NodeJS.ProcessEnv): boolean;
27
34
  //# sourceMappingURL=corpus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"corpus.d.ts","sourceRoot":"","sources":["../../../src/eval/corpus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,EAAE,QAAQ,EAkG5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,QAAQ,EAAE,CAW9E"}
1
+ {"version":3,"file":"corpus.d.ts","sourceRoot":"","sources":["../../../src/eval/corpus.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,EAAE,QAAQ,EAkG5B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,QAAQ,EAAE,CAiB9E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEhF"}
@@ -1,5 +1,6 @@
1
1
  import { NORTHSTAR_CORPUS } from "./northstar-corpus.js";
2
2
  import { OPERATOR_CORPUS } from "./operator-corpus.js";
3
+ import { STANDALONE_CORPUS } from "./standalone-corpus.js";
3
4
  /**
4
5
  * Held-out corpus of GIS workflows for the cross-model eval (honua-server #1956).
5
6
  *
@@ -126,9 +127,24 @@ export function resolveCorpus(env = process.env) {
126
127
  return OPERATOR_CORPUS;
127
128
  case "northstar":
128
129
  return NORTHSTAR_CORPUS;
130
+ case "standalone":
131
+ return STANDALONE_CORPUS;
129
132
  case "all":
133
+ // `all` spans the Honua-surface corpora only. The platform-free standalone
134
+ // corpus targets a DIFFERENT surface (a plain FeatureServer fixture) and is
135
+ // selected explicitly via `standalone`; mixing it here would run its
136
+ // census-specific scenarios against an operator surface.
130
137
  return [...CORPUS, ...OPERATOR_CORPUS, ...NORTHSTAR_CORPUS];
131
138
  default:
132
139
  return CORPUS;
133
140
  }
134
141
  }
142
+ /**
143
+ * Whether a corpus selection targets the PLATFORM-FREE standalone surface (a plain
144
+ * public FeatureServer fixture) rather than the Honua operator/analyst surface.
145
+ * The offline runner uses this to pick the census fixture client so the standalone
146
+ * corpus resolves against real recorded non-Honua data.
147
+ */
148
+ export function isStandaloneCorpus(env = process.env) {
149
+ return (env.HONUA_EVAL_CORPUS ?? "").trim().toLowerCase() === "standalone";
150
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"deterministic.d.ts","sourceRoot":"","sources":["../../../../src/eval/drivers/deterministic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAkB,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE9G;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,QAAQ,CAAC,EAAE,mBAAmB;IAC9B,QAAQ,CAAC,MAAM,EAAG,eAAe,CAAU;IAE3C,WAAW,IAAI,OAAO;IAIhB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAsCzF"}
1
+ {"version":3,"file":"deterministic.d.ts","sourceRoot":"","sources":["../../../../src/eval/drivers/deterministic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAkB,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE9G;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,QAAQ,CAAC,EAAE,mBAAmB;IAC9B,QAAQ,CAAC,MAAM,EAAG,eAAe,CAAU;IAE3C,WAAW,IAAI,OAAO;IAIhB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAoDzF"}
@@ -18,6 +18,19 @@ export class DeterministicDriver {
18
18
  const steps = [];
19
19
  const answerParts = [];
20
20
  let errorCount = 0;
21
+ // Ambiguity/refusal scenarios: the ideal client asks for clarification rather
22
+ // than guessing. Honoring this deterministically lets the offline control
23
+ // exercise refusal/clarification behavior, not just happy-path trajectories.
24
+ if (scenario.clarify) {
25
+ return {
26
+ scenarioId: scenario.id,
27
+ modelId: this.id,
28
+ steps,
29
+ finalAnswer: scenario.clarify.question,
30
+ clarificationRequested: true,
31
+ errorCount,
32
+ };
33
+ }
21
34
  for (const planned of scenario.script) {
22
35
  if (!advertised.has(planned.tool)) {
23
36
  // The scripted tool is not on the surface — record a driver-visible error
@@ -1 +1 @@
1
- {"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../../src/eval/grade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAQjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,GAAG,aAAa,CA2CvF"}
1
+ {"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../../src/eval/grade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAQjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,GAAG,aAAa,CAgEvF"}