From fa55d61b7bb49fbbb6771d4b3f5f90c882ed75fc Mon Sep 17 00:00:00 2001 From: Hillary Mutisya <150286414+hillary-mutisya@users.noreply.github.com> Date: Sun, 25 Oct 2026 16:04:44 -0700 Subject: [PATCH 1/3] Add explicit schema question Views and deferred evidence acquisition Extract standalone question contracts, candidate review and chronology repair, maintained schema Views, typed inventory and page protocols, and sanitized Questions/acquisition controls. Keep discovery explicitly per-View and preserve unrelated memory surfaces. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ts/packages/agents/browser/README.md | 30 + ts/packages/agents/browser/package.json | 2 +- .../src/agent/memoryAcquisitionProgress.mts | 56 + .../agents/browser/src/agent/memoryViews.mts | 67 + .../src/views/client/library/memoryHub.css | 140 ++ .../src/views/client/library/memoryHub.html | 8 + .../views/client/library/memoryHub.test.ts | 23 + .../src/views/client/library/memoryHub.ts | 27 + .../library/memoryHubAcquisitions.test.ts | 151 +++ .../client/library/memoryHubAcquisitions.ts | 191 +++ .../client/library/memoryHubDocumentUi.ts | 30 + .../library/memoryHubInventoryPager.test.ts | 59 + .../client/library/memoryHubInventoryPager.ts | 86 ++ .../library/memoryHubInventoryTestData.ts | 62 + .../views/client/library/memoryHubModel.ts | 37 +- .../client/library/memoryHubQuestions.test.ts | 446 +++++++ .../client/library/memoryHubQuestions.ts | 1128 ++++++++++++++++ .../library/memoryHubQuestionsModel.test.ts | 122 ++ .../client/library/memoryHubQuestionsModel.ts | 142 ++ .../library/memoryHubQuestionsTestData.ts | 43 + .../views/client/library/memoryHubSearch.ts | 4 +- .../client/library/memoryHubViewActivity.ts | 1 + .../client/library/memoryHubViewBuild.ts | 14 + .../client/library/memoryHubViewPages.ts | 47 + .../client/library/memoryHubViews.test.ts | 32 + .../views/client/library/memoryHubViews.ts | 33 +- .../server/features/views/questionSchemas.mts | 149 +++ .../server/features/views/viewValidation.mts | 12 +- .../browser/test/schemaQuestionRpc.test.mjs | 460 +++++++ .../browserControlRpc/src/serviceTypes.ts | 57 + .../browserControlRpc/src/viewRoutes.ts | 2 +- .../agents/browserControlRpc/src/viewRpc.ts | 23 +- .../serviceWorker/serviceWorkerRpcHandlers.ts | 16 + ts/packages/memory/client/README.md | 16 + .../memory/client/src/acquisitionProtocol.ts | 132 ++ ts/packages/memory/client/src/index.ts | 3 + .../memory/client/src/inventoryProtocol.ts | 64 + ts/packages/memory/client/src/memoryClient.ts | 226 +++- ts/packages/memory/client/src/protocol.ts | 1 + .../memory/client/src/schemaViewProtocol.ts | 237 ++++ ts/packages/memory/client/src/viewProtocol.ts | 38 +- .../memory/client/test/schemaViewFixture.json | 110 ++ .../client/test/schemaViewProtocol.spec.mjs | 318 +++++ ts/packages/memory/mcp-server/README.md | 19 + .../mcp-server/src/memoryServiceHost.ts | 2 + .../memory/mcp-server/src/schemaViewTools.ts | 194 +++ .../memory/mcp-server/src/viewTools.ts | 2 + .../test/schemaViewProtocol.spec.ts | 444 +++++++ ts/packages/memory/service/README.md | 43 + .../memory/service/src/acquisitionTypes.ts | 145 ++ ts/packages/memory/service/src/batchImport.ts | 34 + .../memory/service/src/deferredAcquisition.ts | 893 +++++++++++++ .../memory/service/src/fileMemoryService.ts | 1044 +++++++++++++-- ts/packages/memory/service/src/index.ts | 4 + ts/packages/memory/service/src/inventory.ts | 70 + .../memory/service/src/inventoryTypes.ts | 46 + .../memory/service/src/memoryViewsCli.ts | 58 +- ts/packages/memory/service/src/rpcFacade.ts | 36 + ts/packages/memory/service/src/schemaView.ts | 964 ++++++++++++++ .../service/src/schemaViewOperations.ts | 388 ++++++ .../memory/service/src/schemaViewSynthesis.ts | 430 ++++++ .../memory/service/src/schemaViewTypes.ts | 205 +++ ts/packages/memory/service/src/types.ts | 6 +- .../memory/service/src/viewBuildValidation.ts | 81 +- ts/packages/memory/service/src/viewBuilds.ts | 63 +- ts/packages/memory/service/src/viewContent.ts | 8 + .../memory/service/src/viewMaintenance.ts | 3 +- ts/packages/memory/service/src/viewMerge.ts | 78 +- .../memory/service/src/viewPublication.ts | 8 +- .../memory/service/src/viewRelationships.ts | 4 + .../memory/service/src/viewSynthesis.ts | 165 ++- ts/packages/memory/service/src/viewText.ts | 4 +- ts/packages/memory/service/src/viewTypes.ts | 56 +- .../memory/service/src/viewValidation.ts | 52 +- .../service/test/deferredAcquisition.spec.ts | 639 +++++++++ .../service/test/schemaAnswerRepair.spec.ts | 460 +++++++ .../memory/service/test/schemaViews.spec.ts | 1178 +++++++++++++++++ .../memory/service/test/viewDrafts.spec.ts | 10 +- 78 files changed, 12459 insertions(+), 222 deletions(-) create mode 100644 ts/packages/agents/browser/src/agent/memoryAcquisitionProgress.mts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubAcquisitions.test.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubAcquisitions.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubInventoryPager.test.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubInventoryPager.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubInventoryTestData.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubQuestions.test.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubQuestions.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubQuestionsModel.test.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubQuestionsModel.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubQuestionsTestData.ts create mode 100644 ts/packages/agents/browser/src/views/client/library/memoryHubViewPages.ts create mode 100644 ts/packages/agents/browser/src/views/server/features/views/questionSchemas.mts create mode 100644 ts/packages/agents/browser/test/schemaQuestionRpc.test.mjs create mode 100644 ts/packages/memory/client/src/acquisitionProtocol.ts create mode 100644 ts/packages/memory/client/src/inventoryProtocol.ts create mode 100644 ts/packages/memory/client/src/schemaViewProtocol.ts create mode 100644 ts/packages/memory/client/test/schemaViewFixture.json create mode 100644 ts/packages/memory/client/test/schemaViewProtocol.spec.mjs create mode 100644 ts/packages/memory/mcp-server/src/schemaViewTools.ts create mode 100644 ts/packages/memory/mcp-server/test/schemaViewProtocol.spec.ts create mode 100644 ts/packages/memory/service/src/acquisitionTypes.ts create mode 100644 ts/packages/memory/service/src/deferredAcquisition.ts create mode 100644 ts/packages/memory/service/src/inventory.ts create mode 100644 ts/packages/memory/service/src/inventoryTypes.ts create mode 100644 ts/packages/memory/service/src/schemaView.ts create mode 100644 ts/packages/memory/service/src/schemaViewOperations.ts create mode 100644 ts/packages/memory/service/src/schemaViewSynthesis.ts create mode 100644 ts/packages/memory/service/src/schemaViewTypes.ts create mode 100644 ts/packages/memory/service/test/deferredAcquisition.spec.ts create mode 100644 ts/packages/memory/service/test/schemaAnswerRepair.spec.ts create mode 100644 ts/packages/memory/service/test/schemaViews.spec.ts diff --git a/ts/packages/agents/browser/README.md b/ts/packages/agents/browser/README.md index 3abb45e28b..41a4f95c40 100644 --- a/ts/packages/agents/browser/README.md +++ b/ts/packages/agents/browser/README.md @@ -22,6 +22,36 @@ puppeteer helpers). ## Architecture +### Schema-defined questions + +The Memory hub's Questions workspace is a complete projection of versioned +schema-view slots, including proposed and rejected slots. The inspector reviews +candidates and exact source revisions, records schema-validated user assertions, +and edits versioned rubrics without rewriting source evidence. Question and +answer state, review, freshness and coverage are displayed separately. + +Question decisions are ranked, grouped by view and capped at five groups. Dismissal +and one-day snooze use the existing fingerprint preferences; changed evidence +resurfaces decisions, and Questions can restore dismissed decisions. Source +mentions are not independent votes. + +Assisted authoring requires explicit approval to generate a proposal and an +explicit maintenance scope; generated slots still require approval. Discovery is +opt-in, bounded and explicitly invoked. Neither operation authorizes tools. +`memoryGetViewPage` returns deterministic Markdown and a `memory-view:` URI; +Markdown links preserve exact revisions, slots and collection item keys when +opened in the browser. Optional service capabilities and operation errors are +reported explicitly, not interpreted as empty inventories. Question lists use +bounded summary pages; selecting a question separately loads its full evidence. +There is no corpus-wide discovery policy or knowledge-status control. + +Questions also shows acquisition progress and explicit run-once/cancel controls +for existing host-authorized requests. Its RPC projection excludes staged +content, authorized arguments and raw request errors. The workspace cannot admit +acquisition requests, enable periodic work or authorize tools; unsupported and +disabled hosts are reported explicitly. Ingestion receipts link to ordinary +activity and retained-source pages. + ### PDF Understanding Worktree The PDF viewer supports local open/drop, selected-page semantic inspection, diff --git a/ts/packages/agents/browser/package.json b/ts/packages/agents/browser/package.json index 1eeae51fc2..7364875183 100644 --- a/ts/packages/agents/browser/package.json +++ b/ts/packages/agents/browser/package.json @@ -41,7 +41,7 @@ "test:local": "jest && npm run test:crossword && npm run test:views:gateway && npm run test:views:client", "test:pdf": "node --test test/pdf*.spec.mjs test/pdfExtraction/*.spec.mjs test/memoryImportHandlers.spec.mjs test/agentConnectionBoundary.spec.mjs", "test:views:client": "jest --runInBand --testMatch \"**/views/client/**/*.test.ts\"", - "test:views:gateway": "node --test test/viewGateway.test.mjs test/viewDraftGateway.test.mjs test/viewLifecycle.test.mjs test/memoryHub.test.mjs test/memoryHubQuery.test.mjs test/memoryHubSourceDependencies.test.mjs test/memoryHubCapture.test.mjs test/browserCaptureRpc.test.mjs test/memoryHubRunbookImports.test.mjs test/memoryHubWebAnalytics.test.mjs", + "test:views:gateway": "node --test test/viewGateway.test.mjs test/viewDraftGateway.test.mjs test/viewLifecycle.test.mjs test/memoryHub.test.mjs test/memoryHubQuery.test.mjs test/memoryHubSourceDependencies.test.mjs test/memoryHubCapture.test.mjs test/browserCaptureRpc.test.mjs test/memoryHubRunbookImports.test.mjs test/memoryHubWebAnalytics.test.mjs test/schemaQuestionRpc.test.mjs", "tsc:agent": "tsc -p src/agent/tsconfig.json", "tsc:puppeteer": "tsc -p src/puppeteer/tsconfig.json", "tsc:views:client:typecheck": "tsc -p src/views/client/pdf/tsconfig.json && tsc -p src/views/client/library/tsconfig.json", diff --git a/ts/packages/agents/browser/src/agent/memoryAcquisitionProgress.mts b/ts/packages/agents/browser/src/agent/memoryAcquisitionProgress.mts new file mode 100644 index 0000000000..5b9c860e31 --- /dev/null +++ b/ts/packages/agents/browser/src/agent/memoryAcquisitionProgress.mts @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { AcquisitionRequest } from "@typeagent/memory-service"; +import type { MemoryAcquisitionProgress } from "@typeagent/browser-control-rpc/serviceTypes"; + +export function acquisitionProgress( + request: AcquisitionRequest, +): MemoryAcquisitionProgress { + return { + corpusId: request.corpusId, + requestId: request.requestId, + sourceId: request.sourceId, + state: request.state, + createdAt: request.createdAt, + updatedAt: request.updatedAt, + calls: request.calls, + retries: request.retries, + consumers: request.consumers.map( + ({ viewId, slotId, definitionRevision }) => ({ + viewId, + slotId, + definitionRevision, + }), + ), + hasError: Boolean(request.error), + ...(request.receipt + ? { + receipt: { + jobId: request.receipt.jobId, + revisionId: request.receipt.revisionId, + state: request.receipt.state, + }, + } + : {}), + }; +} + +export async function acquisitionOperation( + operation: () => Promise, +): Promise { + try { + return await operation(); + } catch (error) { + const message = error instanceof Error ? error.message : ""; + if (/unavailable|unsupported|not configured/i.test(message)) + throw new Error( + "Acquisition capability is unavailable on this host.", + ); + if (/disabled/i.test(message)) + throw new Error("Acquisition is disabled by host policy."); + throw new Error( + "Acquisition operation failed. Request details are restricted to trusted host diagnostics.", + ); + } +} diff --git a/ts/packages/agents/browser/src/agent/memoryViews.mts b/ts/packages/agents/browser/src/agent/memoryViews.mts index 85c50533b0..345009a99e 100644 --- a/ts/packages/agents/browser/src/agent/memoryViews.mts +++ b/ts/packages/agents/browser/src/agent/memoryViews.mts @@ -7,6 +7,11 @@ import type { MemoryViewService, } from "@typeagent/memory-service"; import { createMemoryServiceRpcFacade } from "@typeagent/memory-service/rpc"; +import type { MemoryAcquisitionProgressService } from "@typeagent/memory-client"; +import { + acquisitionProgress, + acquisitionOperation, +} from "./memoryAcquisitionProgress.mjs"; type ViewMethods = Pick< MemoryCenterInvokeFunctions, @@ -14,6 +19,16 @@ type ViewMethods = Pick< | "memoryQueryTopics" | "memoryRebuildTopics" | "memoryListViews" + | "memoryListQuestions" + | "memoryListQuestionSummaries" + | "memoryGetQuestion" + | "memoryListAcquisitions" + | "memoryRunAcquisitions" + | "memoryCancelAcquisition" + | "memoryEditQuestion" + | "memoryGetViewPage" + | "memoryProposeSchemaView" + | "memoryDiscoverSchemaQuestions" | "memoryGetView" | "memorySaveViewDraft" | "memoryArchiveView" @@ -41,11 +56,63 @@ export function createMemoryViewFunctions( service: () => MemoryService & Partial, ): ViewMethods { const views = () => createMemoryServiceRpcFacade(service()); + const progressClient = () => + service() as MemoryService & + Partial; + function required( + name: K, + ): NonNullable { + const value = service(); + const method = value[name]; + if (typeof method !== "function") + throw new Error( + `Schema-defined questions capability unavailable: ${String(name)}`, + ); + return method.bind(value) as NonNullable; + } return { memoryViewCapabilities: () => service().getCapabilities(), memoryQueryTopics: (request) => views().queryTopics(request), memoryRebuildTopics: ({ corpusId }) => views().rebuildTopics(corpusId), memoryListViews: ({ corpusId }) => views().listViews(corpusId), + memoryListQuestions: ({ corpusId }) => + required("listQuestions")(corpusId), + memoryListQuestionSummaries: (request) => + required("listQuestionSummaries")(request), + memoryGetQuestion: (request) => required("getQuestion")(request), + memoryListAcquisitions: ({ corpusId }) => + acquisitionOperation(async () => { + const client = progressClient(); + if (client.listAcquisitionProgress) + return client.listAcquisitionProgress(corpusId); + return (await required("listAcquisitions")(corpusId)).map( + acquisitionProgress, + ); + }), + memoryRunAcquisitions: ({ corpusId }) => + acquisitionOperation(async () => { + const client = progressClient(); + if (client.runAcquisitionProgress) + return client.runAcquisitionProgress(corpusId); + return (await required("runAcquisitions")(corpusId)).map( + acquisitionProgress, + ); + }), + memoryCancelAcquisition: (request) => + acquisitionOperation(async () => { + const client = progressClient(); + if (client.cancelAcquisitionProgress) + return client.cancelAcquisitionProgress(request); + return acquisitionProgress( + await required("cancelAcquisition")(request), + ); + }), + memoryEditQuestion: (request) => required("editQuestion")(request), + memoryGetViewPage: (request) => required("getViewPage")(request), + memoryProposeSchemaView: (request) => + required("proposeSchemaView")(request), + memoryDiscoverSchemaQuestions: (request) => + required("discoverSchemaQuestions")(request), memoryGetView: (request) => views().getView(request), memorySaveViewDraft: (request) => views().saveViewDraft(request), memoryArchiveView: (request) => views().archiveView(request), diff --git a/ts/packages/agents/browser/src/views/client/library/memoryHub.css b/ts/packages/agents/browser/src/views/client/library/memoryHub.css index 3827176767..19699092a5 100644 --- a/ts/packages/agents/browser/src/views/client/library/memoryHub.css +++ b/ts/packages/agents/browser/src/views/client/library/memoryHub.css @@ -14,6 +14,146 @@ body[data-memory-hub] { --accent-soft-hover: color-mix(in srgb, var(--primary) 16%, var(--surface)); } +#page-questions .hub-qhead, +#page-questions .hub-qfilters, +#page-questions .hub-row-actions, +#page-questions .hub-qmeta, +#page-questions .hub-qdetail-head { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} +#page-questions .hub-qfilters { + margin-bottom: 12px; +} +#page-questions .hub-qfilters input[type="search"] { + flex: 1 1 180px; + max-width: 320px; + min-height: 30px; +} +#page-questions .hub-rows { + display: grid; + gap: 2px; + margin-top: 6px; +} +#page-questions .hub-grow { + flex: 1 1 auto; + min-width: 0; +} +#page-questions .hub-muted, +#page-questions .hub-qmeta { + color: var(--muted); + font-size: 13px; +} +#page-questions .hub-notice { + margin-bottom: 12px; + padding: 8px 12px; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--surface-alt); +} +#page-questions .hub-split { + display: grid; + grid-template-columns: minmax(280px, 2fr) minmax(0, 3fr); + gap: 16px; + align-items: start; +} +#page-questions .hub-qlist { + display: flex; + flex-direction: column; + gap: 6px; + min-width: 0; +} +#page-questions .hub-qrow { + padding: 8px 10px; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--surface); + cursor: pointer; +} +#page-questions .hub-qrow:hover { + background: var(--surface-alt); +} +#page-questions .hub-qrow.selected { + border-color: var(--primary); + background: var(--accent-soft-hover); +} +#page-questions .hub-qrow > button { + display: block; + width: 100%; + padding: 0; + border: 0; + background: none; + color: inherit; + font: inherit; + font-weight: 600; + text-align: left; + cursor: pointer; +} +#page-questions .hub-qmeta { + margin-top: 4px; +} +#page-questions .hub-qmeta small { + flex: 1 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +#page-questions .hub-qdetail { + position: sticky; + top: 8px; + max-height: calc(100vh - 24px); + min-width: 0; + overflow: auto; + padding: 0 4px 16px; +} +#page-questions .hub-qdetail:empty::before { + content: "Select a question to see its answers."; + color: var(--muted); +} +#page-questions .hub-qdetail-head h3 { + flex: 1 1 100%; + margin: 0; + font-size: 16px; +} +#page-questions .hub-qdetail details { + margin: 10px 0; +} +#page-questions summary { + cursor: pointer; + font-weight: 600; +} +#page-questions .hub-fields { + display: grid; + grid-template-columns: max-content 1fr; + gap: 2px 12px; + margin: 0 0 6px; +} +#page-questions .hub-fields dt { + color: var(--muted); + font-size: 13px; +} +#page-questions .hub-fields dd { + margin: 0; +} +#page-questions .hub-panel { + margin-top: 20px; + padding: 10px 14px; + border: 1px solid var(--border); + border-radius: 8px; +} +@media (max-width: 900px) { + #page-questions .hub-split { + grid-template-columns: minmax(0, 1fr); + } + #page-questions .hub-qdetail { + position: static; + max-height: none; + } +} + [hidden] { display: none !important; } diff --git a/ts/packages/agents/browser/src/views/client/library/memoryHub.html b/ts/packages/agents/browser/src/views/client/library/memoryHub.html index 0f28e1d6dd..6eaa5e2bc8 100644 --- a/ts/packages/agents/browser/src/views/client/library/memoryHub.html +++ b/ts/packages/agents/browser/src/views/client/library/memoryHub.html @@ -75,6 +75,10 @@

Memory

>Views + Questions ExploreSearch and Ask

Views

+