@qvac/core 0.0.0 → 0.1.1
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/LICENSE.md +198 -0
- package/NOTICE.md +13 -0
- package/README.md +694 -0
- package/SECURITY.md +6 -0
- package/dist/assistant.bundle +47257 -0
- package/dist/assistant.cjs +18 -0
- package/dist/assistant.d.ts +8 -0
- package/dist/prebuilds-manifest.json +380 -0
- package/dist-lib/client.d.ts +2 -0
- package/dist-lib/client.js +1 -0
- package/dist-lib/index.d.ts +13 -0
- package/dist-lib/index.js +10 -0
- package/dist-lib/lib/agent-icons.d.ts +3 -0
- package/dist-lib/lib/agent-icons.js +1 -0
- package/dist-lib/lib/api/agents.d.ts +5 -0
- package/dist-lib/lib/api/agents.js +38 -0
- package/dist-lib/lib/api/app-config.d.ts +5 -0
- package/dist-lib/lib/api/app-config.js +108 -0
- package/dist-lib/lib/api/approval-rules.d.ts +3 -0
- package/dist-lib/lib/api/approval-rules.js +9 -0
- package/dist-lib/lib/api/attachments.d.ts +8 -0
- package/dist-lib/lib/api/attachments.js +15 -0
- package/dist-lib/lib/api/chat-groups.d.ts +5 -0
- package/dist-lib/lib/api/chat-groups.js +12 -0
- package/dist-lib/lib/api/chat.d.ts +5 -0
- package/dist-lib/lib/api/chat.js +38 -0
- package/dist-lib/lib/api/chunks.d.ts +5 -0
- package/dist-lib/lib/api/chunks.js +61 -0
- package/dist-lib/lib/api/config.d.ts +5 -0
- package/dist-lib/lib/api/config.js +15 -0
- package/dist-lib/lib/api/current-mesh.d.ts +5 -0
- package/dist-lib/lib/api/current-mesh.js +12 -0
- package/dist-lib/lib/api/custom-models.d.ts +5 -0
- package/dist-lib/lib/api/custom-models.js +45 -0
- package/dist-lib/lib/api/devices.d.ts +8 -0
- package/dist-lib/lib/api/devices.js +125 -0
- package/dist-lib/lib/api/diagnostics.d.ts +7 -0
- package/dist-lib/lib/api/diagnostics.js +7 -0
- package/dist-lib/lib/api/effective-ctx-size.d.ts +2 -0
- package/dist-lib/lib/api/effective-ctx-size.js +10 -0
- package/dist-lib/lib/api/execution.d.ts +3 -0
- package/dist-lib/lib/api/execution.js +45 -0
- package/dist-lib/lib/api/files.d.ts +3 -0
- package/dist-lib/lib/api/files.js +48 -0
- package/dist-lib/lib/api/index.d.ts +62 -0
- package/dist-lib/lib/api/index.js +172 -0
- package/dist-lib/lib/api/instrument.d.ts +2 -0
- package/dist-lib/lib/api/instrument.js +86 -0
- package/dist-lib/lib/api/knowledge.d.ts +8 -0
- package/dist-lib/lib/api/knowledge.js +26 -0
- package/dist-lib/lib/api/logs.d.ts +14 -0
- package/dist-lib/lib/api/logs.js +116 -0
- package/dist-lib/lib/api/mesh.d.ts +13 -0
- package/dist-lib/lib/api/mesh.js +25 -0
- package/dist-lib/lib/api/model-download.d.ts +5 -0
- package/dist-lib/lib/api/model-download.js +47 -0
- package/dist-lib/lib/api/network.d.ts +8 -0
- package/dist-lib/lib/api/network.js +6 -0
- package/dist-lib/lib/api/notifications.d.ts +21 -0
- package/dist-lib/lib/api/notifications.js +108 -0
- package/dist-lib/lib/api/rag.d.ts +7 -0
- package/dist-lib/lib/api/rag.js +216 -0
- package/dist-lib/lib/api/sync.d.ts +3 -0
- package/dist-lib/lib/api/sync.js +6 -0
- package/dist-lib/lib/api/tool-activity.d.ts +5 -0
- package/dist-lib/lib/api/tool-activity.js +53 -0
- package/dist-lib/lib/api/tool-configs.d.ts +6 -0
- package/dist-lib/lib/api/tool-configs.js +27 -0
- package/dist-lib/lib/api/watchable.d.ts +12 -0
- package/dist-lib/lib/api/watchable.js +121 -0
- package/dist-lib/lib/assistant-client.d.ts +21 -0
- package/dist-lib/lib/assistant-client.js +52 -0
- package/dist-lib/lib/assistant-tree.d.ts +50 -0
- package/dist-lib/lib/assistant-tree.js +184 -0
- package/dist-lib/lib/assistant.d.ts +425 -0
- package/dist-lib/lib/assistant.js +2706 -0
- package/dist-lib/lib/audio-format.d.ts +4 -0
- package/dist-lib/lib/audio-format.js +4 -0
- package/dist-lib/lib/chat-title.d.ts +5 -0
- package/dist-lib/lib/chat-title.js +58 -0
- package/dist-lib/lib/client.d.ts +25 -0
- package/dist-lib/lib/client.js +53 -0
- package/dist-lib/lib/collabora/convert.d.ts +9 -0
- package/dist-lib/lib/collabora/convert.js +62 -0
- package/dist-lib/lib/collabora/entry.d.ts +1 -0
- package/dist-lib/lib/collabora/entry.js +44 -0
- package/dist-lib/lib/collabora/protocol.d.ts +15 -0
- package/dist-lib/lib/collabora/protocol.js +24 -0
- package/dist-lib/lib/collabora/worker.d.ts +5 -0
- package/dist-lib/lib/collabora/worker.js +111 -0
- package/dist-lib/lib/compaction.d.ts +30 -0
- package/dist-lib/lib/compaction.js +95 -0
- package/dist-lib/lib/constants.d.ts +4 -0
- package/dist-lib/lib/constants.js +4 -0
- package/dist-lib/lib/core/agent.d.ts +15 -0
- package/dist-lib/lib/core/agent.js +149 -0
- package/dist-lib/lib/core/app-build.d.ts +7 -0
- package/dist-lib/lib/core/app-build.js +1 -0
- package/dist-lib/lib/core/approval-rules.d.ts +15 -0
- package/dist-lib/lib/core/approval-rules.js +41 -0
- package/dist-lib/lib/core/async-iterable.d.ts +13 -0
- package/dist-lib/lib/core/async-iterable.js +65 -0
- package/dist-lib/lib/core/attachment-link.d.ts +7 -0
- package/dist-lib/lib/core/attachment-link.js +6 -0
- package/dist-lib/lib/core/audio-attachment.d.ts +1 -0
- package/dist-lib/lib/core/audio-attachment.js +4 -0
- package/dist-lib/lib/core/blind-peers.d.ts +6 -0
- package/dist-lib/lib/core/blind-peers.js +13 -0
- package/dist-lib/lib/core/chat-activity.d.ts +7 -0
- package/dist-lib/lib/core/chat-activity.js +244 -0
- package/dist-lib/lib/core/chat-group.d.ts +14 -0
- package/dist-lib/lib/core/chat-group.js +79 -0
- package/dist-lib/lib/core/chat-search.d.ts +17 -0
- package/dist-lib/lib/core/chat-search.js +199 -0
- package/dist-lib/lib/core/chat.d.ts +37 -0
- package/dist-lib/lib/core/chat.js +196 -0
- package/dist-lib/lib/core/chunk.d.ts +61 -0
- package/dist-lib/lib/core/chunk.js +212 -0
- package/dist-lib/lib/core/contract/activity.d.ts +11 -0
- package/dist-lib/lib/core/contract/activity.js +45 -0
- package/dist-lib/lib/core/contract/agents.d.ts +2 -0
- package/dist-lib/lib/core/contract/agents.js +29 -0
- package/dist-lib/lib/core/contract/approval-rules.d.ts +2 -0
- package/dist-lib/lib/core/contract/approval-rules.js +14 -0
- package/dist-lib/lib/core/contract/attestations.d.ts +2 -0
- package/dist-lib/lib/core/contract/attestations.js +14 -0
- package/dist-lib/lib/core/contract/chat-group.d.ts +2 -0
- package/dist-lib/lib/core/contract/chat-group.js +20 -0
- package/dist-lib/lib/core/contract/chat.d.ts +2 -0
- package/dist-lib/lib/core/contract/chat.js +18 -0
- package/dist-lib/lib/core/contract/chunks.d.ts +2 -0
- package/dist-lib/lib/core/contract/chunks.js +156 -0
- package/dist-lib/lib/core/contract/devices.d.ts +2 -0
- package/dist-lib/lib/core/contract/devices.js +110 -0
- package/dist-lib/lib/core/contract/index.d.ts +2 -0
- package/dist-lib/lib/core/contract/index.js +30 -0
- package/dist-lib/lib/core/contract/indexing.d.ts +4 -0
- package/dist-lib/lib/core/contract/indexing.js +82 -0
- package/dist-lib/lib/core/contract/knowledge.d.ts +2 -0
- package/dist-lib/lib/core/contract/knowledge.js +55 -0
- package/dist-lib/lib/core/contract/members.d.ts +2 -0
- package/dist-lib/lib/core/contract/members.js +96 -0
- package/dist-lib/lib/core/contract/seed-defaults.d.ts +2 -0
- package/dist-lib/lib/core/contract/seed-defaults.js +29 -0
- package/dist-lib/lib/core/contract/tool-activity.d.ts +2 -0
- package/dist-lib/lib/core/contract/tool-activity.js +25 -0
- package/dist-lib/lib/core/contract/tool-configs.d.ts +2 -0
- package/dist-lib/lib/core/contract/tool-configs.js +50 -0
- package/dist-lib/lib/core/contract/types.d.ts +11 -0
- package/dist-lib/lib/core/contract/types.js +1 -0
- package/dist-lib/lib/core/contract/writers.d.ts +2 -0
- package/dist-lib/lib/core/contract/writers.js +112 -0
- package/dist-lib/lib/core/cpu-info-node.d.ts +3 -0
- package/dist-lib/lib/core/cpu-info-node.js +2 -0
- package/dist-lib/lib/core/departures.d.ts +41 -0
- package/dist-lib/lib/core/departures.js +145 -0
- package/dist-lib/lib/core/device-attestation.d.ts +6 -0
- package/dist-lib/lib/core/device-attestation.js +12 -0
- package/dist-lib/lib/core/devices.d.ts +14 -0
- package/dist-lib/lib/core/devices.js +11 -0
- package/dist-lib/lib/core/document-attachment.d.ts +22 -0
- package/dist-lib/lib/core/document-attachment.js +35 -0
- package/dist-lib/lib/core/files/attachments.d.ts +8 -0
- package/dist-lib/lib/core/files/attachments.js +50 -0
- package/dist-lib/lib/core/files/index.d.ts +133 -0
- package/dist-lib/lib/core/files/index.js +585 -0
- package/dist-lib/lib/core/files/knowledge.d.ts +9 -0
- package/dist-lib/lib/core/files/knowledge.js +31 -0
- package/dist-lib/lib/core/files/preview/image.d.ts +4 -0
- package/dist-lib/lib/core/files/preview/image.js +42 -0
- package/dist-lib/lib/core/files/preview/index.d.ts +33 -0
- package/dist-lib/lib/core/files/preview/index.js +161 -0
- package/dist-lib/lib/core/files/processing/claims.d.ts +26 -0
- package/dist-lib/lib/core/files/processing/claims.js +89 -0
- package/dist-lib/lib/core/files/processing/document.d.ts +100 -0
- package/dist-lib/lib/core/files/processing/document.js +101 -0
- package/dist-lib/lib/core/files/processing/index.d.ts +75 -0
- package/dist-lib/lib/core/files/processing/index.js +295 -0
- package/dist-lib/lib/core/files/processing/ownership.d.ts +11 -0
- package/dist-lib/lib/core/files/processing/ownership.js +41 -0
- package/dist-lib/lib/core/files/processing/protocol.d.ts +29 -0
- package/dist-lib/lib/core/files/processing/protocol.js +70 -0
- package/dist-lib/lib/core/files/processing/settle.d.ts +10 -0
- package/dist-lib/lib/core/files/processing/settle.js +75 -0
- package/dist-lib/lib/core/files/processing/source.d.ts +36 -0
- package/dist-lib/lib/core/files/processing/source.js +96 -0
- package/dist-lib/lib/core/files/processing/state.d.ts +64 -0
- package/dist-lib/lib/core/files/processing/state.js +118 -0
- package/dist-lib/lib/core/files/rag/artifact/codec.d.ts +3 -0
- package/dist-lib/lib/core/files/rag/artifact/codec.js +172 -0
- package/dist-lib/lib/core/files/rag/artifact/id.d.ts +10 -0
- package/dist-lib/lib/core/files/rag/artifact/id.js +39 -0
- package/dist-lib/lib/core/files/rag/artifact/types.d.ts +24 -0
- package/dist-lib/lib/core/files/rag/artifact/types.js +41 -0
- package/dist-lib/lib/core/files/rag/build.d.ts +25 -0
- package/dist-lib/lib/core/files/rag/build.js +60 -0
- package/dist-lib/lib/core/files/rag/chunk/chunking.d.ts +12 -0
- package/dist-lib/lib/core/files/rag/chunk/chunking.js +117 -0
- package/dist-lib/lib/core/files/rag/chunk/metadata.d.ts +30 -0
- package/dist-lib/lib/core/files/rag/chunk/metadata.js +89 -0
- package/dist-lib/lib/core/files/rag/chunk/tokenizer.d.ts +7 -0
- package/dist-lib/lib/core/files/rag/chunk/tokenizer.js +18 -0
- package/dist-lib/lib/core/files/rag/document-types.d.ts +11 -0
- package/dist-lib/lib/core/files/rag/document-types.js +94 -0
- package/dist-lib/lib/core/files/rag/extract/csv-text.d.ts +1 -0
- package/dist-lib/lib/core/files/rag/extract/csv-text.js +117 -0
- package/dist-lib/lib/core/files/rag/extract/document-types.d.ts +22 -0
- package/dist-lib/lib/core/files/rag/extract/document-types.js +152 -0
- package/dist-lib/lib/core/files/rag/extract/extract.d.ts +17 -0
- package/dist-lib/lib/core/files/rag/extract/extract.js +81 -0
- package/dist-lib/lib/core/files/rag/extract/html-table-text.d.ts +1 -0
- package/dist-lib/lib/core/files/rag/extract/html-table-text.js +125 -0
- package/dist-lib/lib/core/files/rag/extract/html-text.d.ts +1 -0
- package/dist-lib/lib/core/files/rag/extract/html-text.js +132 -0
- package/dist-lib/lib/core/files/rag/extract/office-collabora.d.ts +2 -0
- package/dist-lib/lib/core/files/rag/extract/office-collabora.js +8 -0
- package/dist-lib/lib/core/files/rag/extract/pdf-platform.d.ts +17 -0
- package/dist-lib/lib/core/files/rag/extract/pdf-platform.js +141 -0
- package/dist-lib/lib/core/files/rag/extract/pdf-text.d.ts +12 -0
- package/dist-lib/lib/core/files/rag/extract/pdf-text.js +43 -0
- package/dist-lib/lib/core/files/rag/extract/rtf-text.d.ts +1 -0
- package/dist-lib/lib/core/files/rag/extract/rtf-text.js +15 -0
- package/dist-lib/lib/core/files/rag/extract/text-decoding.d.ts +1 -0
- package/dist-lib/lib/core/files/rag/extract/text-decoding.js +91 -0
- package/dist-lib/lib/core/files/rag/extract/types.d.ts +36 -0
- package/dist-lib/lib/core/files/rag/extract/types.js +12 -0
- package/dist-lib/lib/core/files/rag/extract/utf.d.ts +2 -0
- package/dist-lib/lib/core/files/rag/extract/utf.js +18 -0
- package/dist-lib/lib/core/files/rag/index.d.ts +54 -0
- package/dist-lib/lib/core/files/rag/index.js +79 -0
- package/dist-lib/lib/core/files/rag/limits.d.ts +2 -0
- package/dist-lib/lib/core/files/rag/limits.js +2 -0
- package/dist-lib/lib/core/files/rag/load.d.ts +28 -0
- package/dist-lib/lib/core/files/rag/load.js +250 -0
- package/dist-lib/lib/core/files/rag/models.d.ts +1 -0
- package/dist-lib/lib/core/files/rag/models.js +1 -0
- package/dist-lib/lib/core/files/rag/policy.d.ts +2 -0
- package/dist-lib/lib/core/files/rag/policy.js +23 -0
- package/dist-lib/lib/core/files/rag/produce.d.ts +21 -0
- package/dist-lib/lib/core/files/rag/produce.js +214 -0
- package/dist-lib/lib/core/files/rag/retrieval/citation-match.d.ts +2 -0
- package/dist-lib/lib/core/files/rag/retrieval/citation-match.js +16 -0
- package/dist-lib/lib/core/files/rag/retrieval/coverage.d.ts +4 -0
- package/dist-lib/lib/core/files/rag/retrieval/coverage.js +48 -0
- package/dist-lib/lib/core/files/rag/retrieval/diversity.d.ts +10 -0
- package/dist-lib/lib/core/files/rag/retrieval/diversity.js +37 -0
- package/dist-lib/lib/core/files/rag/retrieval/floor.d.ts +5 -0
- package/dist-lib/lib/core/files/rag/retrieval/floor.js +21 -0
- package/dist-lib/lib/core/files/rag/retrieval/format.d.ts +6 -0
- package/dist-lib/lib/core/files/rag/retrieval/format.js +21 -0
- package/dist-lib/lib/core/files/rag/retrieval/full-text.d.ts +22 -0
- package/dist-lib/lib/core/files/rag/retrieval/full-text.js +137 -0
- package/dist-lib/lib/core/files/rag/retrieval/knowledge.d.ts +2 -0
- package/dist-lib/lib/core/files/rag/retrieval/knowledge.js +53 -0
- package/dist-lib/lib/core/files/rag/retrieval/notices.d.ts +16 -0
- package/dist-lib/lib/core/files/rag/retrieval/notices.js +27 -0
- package/dist-lib/lib/core/files/rag/retrieval/scoring.d.ts +8 -0
- package/dist-lib/lib/core/files/rag/retrieval/scoring.js +82 -0
- package/dist-lib/lib/core/files/rag/retrieval/search.d.ts +3 -0
- package/dist-lib/lib/core/files/rag/retrieval/search.js +46 -0
- package/dist-lib/lib/core/files/rag/retrieval/selection.d.ts +10 -0
- package/dist-lib/lib/core/files/rag/retrieval/selection.js +63 -0
- package/dist-lib/lib/core/files/rag/retrieval/source-tag-filter.d.ts +6 -0
- package/dist-lib/lib/core/files/rag/retrieval/source-tag-filter.js +79 -0
- package/dist-lib/lib/core/files/rag/retrieval/sources.d.ts +13 -0
- package/dist-lib/lib/core/files/rag/retrieval/sources.js +20 -0
- package/dist-lib/lib/core/files/rag/retrieval/target.d.ts +13 -0
- package/dist-lib/lib/core/files/rag/retrieval/target.js +89 -0
- package/dist-lib/lib/core/files/rag/retrieval/terms.d.ts +8 -0
- package/dist-lib/lib/core/files/rag/retrieval/terms.js +70 -0
- package/dist-lib/lib/core/files/rag/retrieval/types.d.ts +28 -0
- package/dist-lib/lib/core/files/rag/retrieval/types.js +1 -0
- package/dist-lib/lib/core/files/rag/retrieval/web-sources.d.ts +8 -0
- package/dist-lib/lib/core/files/rag/retrieval/web-sources.js +41 -0
- package/dist-lib/lib/core/files/rag/retrieval/whole-document-query.d.ts +1 -0
- package/dist-lib/lib/core/files/rag/retrieval/whole-document-query.js +96 -0
- package/dist-lib/lib/core/files/rag/vector/memory.d.ts +15 -0
- package/dist-lib/lib/core/files/rag/vector/memory.js +135 -0
- package/dist-lib/lib/core/files/rag/vector/types.d.ts +28 -0
- package/dist-lib/lib/core/files/rag/vector/types.js +1 -0
- package/dist-lib/lib/core/files/records/chunks.d.ts +3 -0
- package/dist-lib/lib/core/files/records/chunks.js +12 -0
- package/dist-lib/lib/core/files/records/index.d.ts +10 -0
- package/dist-lib/lib/core/files/records/index.js +8 -0
- package/dist-lib/lib/core/files/records/knowledge.d.ts +22 -0
- package/dist-lib/lib/core/files/records/knowledge.js +105 -0
- package/dist-lib/lib/core/files/records/linkage.d.ts +11 -0
- package/dist-lib/lib/core/files/records/linkage.js +27 -0
- package/dist-lib/lib/core/files/records/output.d.ts +8 -0
- package/dist-lib/lib/core/files/records/output.js +38 -0
- package/dist-lib/lib/core/files/records/reclaim.d.ts +5 -0
- package/dist-lib/lib/core/files/records/reclaim.js +73 -0
- package/dist-lib/lib/core/files/records/routing.d.ts +23 -0
- package/dist-lib/lib/core/files/records/routing.js +62 -0
- package/dist-lib/lib/core/files/records/source.d.ts +16 -0
- package/dist-lib/lib/core/files/records/source.js +70 -0
- package/dist-lib/lib/core/files/records/sweep.d.ts +2 -0
- package/dist-lib/lib/core/files/records/sweep.js +30 -0
- package/dist-lib/lib/core/files/store/blob.d.ts +29 -0
- package/dist-lib/lib/core/files/store/blob.js +98 -0
- package/dist-lib/lib/core/files/store/index.d.ts +36 -0
- package/dist-lib/lib/core/files/store/index.js +89 -0
- package/dist-lib/lib/core/files/store/inline.d.ts +8 -0
- package/dist-lib/lib/core/files/store/inline.js +38 -0
- package/dist-lib/lib/core/files/store/reference.d.ts +27 -0
- package/dist-lib/lib/core/files/store/reference.js +168 -0
- package/dist-lib/lib/core/files/store/server.d.ts +27 -0
- package/dist-lib/lib/core/files/store/server.js +114 -0
- package/dist-lib/lib/core/files/store/sweep.d.ts +2 -0
- package/dist-lib/lib/core/files/store/sweep.js +45 -0
- package/dist-lib/lib/core/files/upload-input.d.ts +27 -0
- package/dist-lib/lib/core/files/upload-input.js +52 -0
- package/dist-lib/lib/core/files/upload.d.ts +32 -0
- package/dist-lib/lib/core/files/upload.js +201 -0
- package/dist-lib/lib/core/fs-drives.d.ts +18 -0
- package/dist-lib/lib/core/fs-drives.js +58 -0
- package/dist-lib/lib/core/gpu-info-node.d.ts +3 -0
- package/dist-lib/lib/core/gpu-info-node.js +2 -0
- package/dist-lib/lib/core/handoff-channel.d.ts +46 -0
- package/dist-lib/lib/core/handoff-channel.js +102 -0
- package/dist-lib/lib/core/health-assistant.d.ts +7 -0
- package/dist-lib/lib/core/health-assistant.js +24 -0
- package/dist-lib/lib/core/helpers/index.d.ts +5 -0
- package/dist-lib/lib/core/helpers/index.js +74 -0
- package/dist-lib/lib/core/hosted-agents.d.ts +36 -0
- package/dist-lib/lib/core/hosted-agents.js +127 -0
- package/dist-lib/lib/core/id.d.ts +1 -0
- package/dist-lib/lib/core/id.js +5 -0
- package/dist-lib/lib/core/identity.d.ts +14 -0
- package/dist-lib/lib/core/identity.js +45 -0
- package/dist-lib/lib/core/image/cache-sweep.d.ts +12 -0
- package/dist-lib/lib/core/image/cache-sweep.js +67 -0
- package/dist-lib/lib/core/image/normalize.d.ts +3 -0
- package/dist-lib/lib/core/image/normalize.js +49 -0
- package/dist-lib/lib/core/image/sniff.d.ts +3 -0
- package/dist-lib/lib/core/image/sniff.js +149 -0
- package/dist-lib/lib/core/invite.d.ts +14 -0
- package/dist-lib/lib/core/invite.js +36 -0
- package/dist-lib/lib/core/local/index.d.ts +71 -0
- package/dist-lib/lib/core/local/index.js +391 -0
- package/dist-lib/lib/core/membership.d.ts +15 -0
- package/dist-lib/lib/core/membership.js +40 -0
- package/dist-lib/lib/core/mesh.d.ts +153 -0
- package/dist-lib/lib/core/mesh.js +509 -0
- package/dist-lib/lib/core/migrations/agent-catalog.d.ts +6 -0
- package/dist-lib/lib/core/migrations/agent-catalog.js +17 -0
- package/dist-lib/lib/core/migrations/index.d.ts +5 -0
- package/dist-lib/lib/core/migrations/index.js +7 -0
- package/dist-lib/lib/core/migrations/membership.d.ts +11 -0
- package/dist-lib/lib/core/migrations/membership.js +26 -0
- package/dist-lib/lib/core/min-version.d.ts +6 -0
- package/dist-lib/lib/core/min-version.js +31 -0
- package/dist-lib/lib/core/next-frame.d.ts +6 -0
- package/dist-lib/lib/core/next-frame.js +27 -0
- package/dist-lib/lib/core/notification.d.ts +6 -0
- package/dist-lib/lib/core/notification.js +162 -0
- package/dist-lib/lib/core/ota-config.d.ts +19 -0
- package/dist-lib/lib/core/ota-config.js +44 -0
- package/dist-lib/lib/core/prompt-history.d.ts +12 -0
- package/dist-lib/lib/core/prompt-history.js +40 -0
- package/dist-lib/lib/core/search-fold.d.ts +1 -0
- package/dist-lib/lib/core/search-fold.js +4 -0
- package/dist-lib/lib/core/secret-handoff.d.ts +17 -0
- package/dist-lib/lib/core/secret-handoff.js +29 -0
- package/dist-lib/lib/core/send.d.ts +18 -0
- package/dist-lib/lib/core/send.js +46 -0
- package/dist-lib/lib/core/skill-overlay-handoff.d.ts +25 -0
- package/dist-lib/lib/core/skill-overlay-handoff.js +50 -0
- package/dist-lib/lib/core/skill-overlay-receiver.d.ts +8 -0
- package/dist-lib/lib/core/skill-overlay-receiver.js +20 -0
- package/dist-lib/lib/core/skill-secrets.d.ts +25 -0
- package/dist-lib/lib/core/skill-secrets.js +79 -0
- package/dist-lib/lib/core/sync/chats.d.ts +3 -0
- package/dist-lib/lib/core/sync/chats.js +5 -0
- package/dist-lib/lib/core/sync/chunks.d.ts +9 -0
- package/dist-lib/lib/core/sync/chunks.js +16 -0
- package/dist-lib/lib/core/sync/devices.d.ts +4 -0
- package/dist-lib/lib/core/sync/devices.js +12 -0
- package/dist-lib/lib/core/sync/files.d.ts +16 -0
- package/dist-lib/lib/core/sync/files.js +109 -0
- package/dist-lib/lib/core/sync/index.d.ts +5 -0
- package/dist-lib/lib/core/sync/index.js +63 -0
- package/dist-lib/lib/core/sync-status.d.ts +17 -0
- package/dist-lib/lib/core/sync-status.js +129 -0
- package/dist-lib/lib/core/system-info.d.ts +24 -0
- package/dist-lib/lib/core/system-info.js +66 -0
- package/dist-lib/lib/core/tool-activity.d.ts +22 -0
- package/dist-lib/lib/core/tool-activity.js +39 -0
- package/dist-lib/lib/core/tool-configs.d.ts +13 -0
- package/dist-lib/lib/core/tool-configs.js +141 -0
- package/dist-lib/lib/core.d.ts +291 -0
- package/dist-lib/lib/core.js +2126 -0
- package/dist-lib/lib/credential-pusher.d.ts +67 -0
- package/dist-lib/lib/credential-pusher.js +290 -0
- package/dist-lib/lib/custom-model-pusher.d.ts +9 -0
- package/dist-lib/lib/custom-model-pusher.js +42 -0
- package/dist-lib/lib/custom-model-source.d.ts +15 -0
- package/dist-lib/lib/custom-model-source.js +46 -0
- package/dist-lib/lib/deadline.d.ts +9 -0
- package/dist-lib/lib/deadline.js +31 -0
- package/dist-lib/lib/diagnostics.d.ts +35 -0
- package/dist-lib/lib/diagnostics.js +14 -0
- package/dist-lib/lib/fetch-node.d.ts +1 -0
- package/dist-lib/lib/fetch-node.js +1 -0
- package/dist-lib/lib/fs-drive-bridge.d.ts +12 -0
- package/dist-lib/lib/fs-drive-bridge.js +186 -0
- package/dist-lib/lib/fs-grant-keys.d.ts +2 -0
- package/dist-lib/lib/fs-grant-keys.js +6 -0
- package/dist-lib/lib/fs-grants.d.ts +10 -0
- package/dist-lib/lib/fs-grants.js +44 -0
- package/dist-lib/lib/image-cache-dir.d.ts +1 -0
- package/dist-lib/lib/image-cache-dir.js +5 -0
- package/dist-lib/lib/integrations/index.d.ts +4 -0
- package/dist-lib/lib/integrations/index.js +3 -0
- package/dist-lib/lib/integrations/mcp-providers.d.ts +12 -0
- package/dist-lib/lib/integrations/mcp-providers.js +22 -0
- package/dist-lib/lib/integrations/oauth-providers.d.ts +14 -0
- package/dist-lib/lib/integrations/oauth-providers.js +116 -0
- package/dist-lib/lib/integrations/setup-routes.d.ts +12 -0
- package/dist-lib/lib/integrations/setup-routes.js +114 -0
- package/dist-lib/lib/knowledge-search.d.ts +7 -0
- package/dist-lib/lib/knowledge-search.js +53 -0
- package/dist-lib/lib/kv-cache-reclaim.d.ts +5 -0
- package/dist-lib/lib/kv-cache-reclaim.js +29 -0
- package/dist-lib/lib/kv-cache.d.ts +18 -0
- package/dist-lib/lib/kv-cache.js +33 -0
- package/dist-lib/lib/legacy-core-domain.d.ts +258 -0
- package/dist-lib/lib/legacy-core-domain.js +1 -0
- package/dist-lib/lib/llm-model-names.d.ts +1 -0
- package/dist-lib/lib/llm-model-names.js +1 -0
- package/dist-lib/lib/log-buffer.d.ts +7 -0
- package/dist-lib/lib/log-buffer.js +16 -0
- package/dist-lib/lib/log-levels.d.ts +7 -0
- package/dist-lib/lib/log-levels.js +1 -0
- package/dist-lib/lib/log.d.ts +60 -0
- package/dist-lib/lib/log.js +149 -0
- package/dist-lib/lib/logger-type.d.ts +13 -0
- package/dist-lib/lib/logger-type.js +1 -0
- package/dist-lib/lib/logger.d.ts +5 -0
- package/dist-lib/lib/logger.js +18 -0
- package/dist-lib/lib/model-catalog-revision.d.ts +9 -0
- package/dist-lib/lib/model-catalog-revision.js +65 -0
- package/dist-lib/lib/model-clamp.d.ts +6 -0
- package/dist-lib/lib/model-clamp.js +20 -0
- package/dist-lib/lib/model-context.d.ts +13 -0
- package/dist-lib/lib/model-context.js +71 -0
- package/dist-lib/lib/model-download-status.d.ts +29 -0
- package/dist-lib/lib/model-download-status.js +56 -0
- package/dist-lib/lib/model-download.d.ts +191 -0
- package/dist-lib/lib/model-download.js +787 -0
- package/dist-lib/lib/oauth/asana.d.ts +35 -0
- package/dist-lib/lib/oauth/asana.js +71 -0
- package/dist-lib/lib/oauth/browser-connect.d.ts +22 -0
- package/dist-lib/lib/oauth/browser-connect.js +83 -0
- package/dist-lib/lib/oauth/client.d.ts +9 -0
- package/dist-lib/lib/oauth/client.js +9 -0
- package/dist-lib/lib/oauth/common.d.ts +38 -0
- package/dist-lib/lib/oauth/common.js +36 -0
- package/dist-lib/lib/oauth/crypto.d.ts +3 -0
- package/dist-lib/lib/oauth/crypto.js +25 -0
- package/dist-lib/lib/oauth/fetch-platform.d.ts +1 -0
- package/dist-lib/lib/oauth/fetch-platform.js +1 -0
- package/dist-lib/lib/oauth/flow-core.d.ts +6 -0
- package/dist-lib/lib/oauth/flow-core.js +37 -0
- package/dist-lib/lib/oauth/flow.d.ts +3 -0
- package/dist-lib/lib/oauth/flow.js +24 -0
- package/dist-lib/lib/oauth/google.d.ts +37 -0
- package/dist-lib/lib/oauth/google.js +77 -0
- package/dist-lib/lib/oauth/host.d.ts +5 -0
- package/dist-lib/lib/oauth/host.js +3 -0
- package/dist-lib/lib/oauth/keys.d.ts +25 -0
- package/dist-lib/lib/oauth/keys.js +93 -0
- package/dist-lib/lib/oauth/loopback.d.ts +3 -0
- package/dist-lib/lib/oauth/loopback.js +119 -0
- package/dist-lib/lib/oauth/loopback.stub.d.ts +3 -0
- package/dist-lib/lib/oauth/loopback.stub.js +6 -0
- package/dist-lib/lib/oauth/notion.d.ts +34 -0
- package/dist-lib/lib/oauth/notion.js +82 -0
- package/dist-lib/lib/oauth/pkce-runtime.d.ts +3 -0
- package/dist-lib/lib/oauth/pkce-runtime.js +5 -0
- package/dist-lib/lib/oauth/pkce.d.ts +8 -0
- package/dist-lib/lib/oauth/pkce.js +6 -0
- package/dist-lib/lib/oauth/providers.d.ts +5 -0
- package/dist-lib/lib/oauth/providers.js +73 -0
- package/dist-lib/lib/oauth/redirect-types.d.ts +19 -0
- package/dist-lib/lib/oauth/redirect-types.js +1 -0
- package/dist-lib/lib/oauth/register.d.ts +3 -0
- package/dist-lib/lib/oauth/register.js +34 -0
- package/dist-lib/lib/oauth/resolve.d.ts +17 -0
- package/dist-lib/lib/oauth/resolve.js +57 -0
- package/dist-lib/lib/oauth/spotify.d.ts +32 -0
- package/dist-lib/lib/oauth/spotify.js +73 -0
- package/dist-lib/lib/oauth/token-exchange-core.d.ts +6 -0
- package/dist-lib/lib/oauth/token-exchange-core.js +97 -0
- package/dist-lib/lib/oauth/token-exchange.d.ts +5 -0
- package/dist-lib/lib/oauth/token-exchange.js +9 -0
- package/dist-lib/lib/oauth/types.d.ts +54 -0
- package/dist-lib/lib/oauth/types.js +1 -0
- package/dist-lib/lib/oauth.client.d.ts +5 -0
- package/dist-lib/lib/oauth.client.js +2 -0
- package/dist-lib/lib/oauth.d.ts +14 -0
- package/dist-lib/lib/oauth.js +12 -0
- package/dist-lib/lib/obsidian-key.d.ts +1 -0
- package/dist-lib/lib/obsidian-key.js +1 -0
- package/dist-lib/lib/race-resource.d.ts +5 -0
- package/dist-lib/lib/race-resource.js +25 -0
- package/dist-lib/lib/reconnecting-watch.d.ts +2 -0
- package/dist-lib/lib/reconnecting-watch.js +78 -0
- package/dist-lib/lib/retry-budget.d.ts +14 -0
- package/dist-lib/lib/retry-budget.js +39 -0
- package/dist-lib/lib/run-notices.d.ts +23 -0
- package/dist-lib/lib/run-notices.js +33 -0
- package/dist-lib/lib/run-transcript.d.ts +35 -0
- package/dist-lib/lib/run-transcript.js +106 -0
- package/dist-lib/lib/skill-readiness.d.ts +2 -0
- package/dist-lib/lib/skill-readiness.js +21 -0
- package/dist-lib/lib/skill-types.d.ts +70 -0
- package/dist-lib/lib/skill-types.js +1 -0
- package/dist-lib/lib/spec-loader.d.ts +14 -0
- package/dist-lib/lib/spec-loader.js +9 -0
- package/dist-lib/lib/suspend-lifecycle.d.ts +17 -0
- package/dist-lib/lib/suspend-lifecycle.js +33 -0
- package/dist-lib/lib/task.d.ts +26 -0
- package/dist-lib/lib/task.js +69 -0
- package/dist-lib/lib/unref.d.ts +3 -0
- package/dist-lib/lib/unref.js +4 -0
- package/dist-lib/lib/wav-duration.d.ts +1 -0
- package/dist-lib/lib/wav-duration.js +53 -0
- package/dist-lib/lib/write-queue.d.ts +22 -0
- package/dist-lib/lib/write-queue.js +39 -0
- package/dist-lib/schema/build.d.ts +1 -0
- package/dist-lib/schema/build.js +120 -0
- package/dist-lib/schema/config.d.ts +4 -0
- package/dist-lib/schema/config.js +7 -0
- package/dist-lib/schema/content.d.ts +20 -0
- package/dist-lib/schema/content.js +122 -0
- package/dist-lib/schema/engine.d.ts +2 -0
- package/dist-lib/schema/engine.js +2013 -0
- package/dist-lib/schema/field-order.d.ts +1 -0
- package/dist-lib/schema/field-order.js +95 -0
- package/dist-lib/schema/helpers.d.ts +28 -0
- package/dist-lib/schema/helpers.js +176 -0
- package/dist-lib/schema/local.d.ts +2 -0
- package/dist-lib/schema/local.js +133 -0
- package/dist-lib/schema/qvac.d.ts +22 -0
- package/dist-lib/schema/qvac.js +722 -0
- package/dist-lib/schema/vendor.d.ts +5 -0
- package/dist-lib/spec/bind.d.ts +2 -0
- package/dist-lib/spec/bind.js +520 -0
- package/dist-lib/spec/calls.d.ts +3 -0
- package/dist-lib/spec/calls.js +129 -0
- package/dist-lib/spec/capabilities.d.ts +263 -0
- package/dist-lib/spec/config/hyperconf/index.d.ts +15 -0
- package/dist-lib/spec/config/hyperconf/index.js +24 -0
- package/dist-lib/spec/config/hyperconf/package.json +3 -0
- package/dist-lib/spec/config/hyperschema/index.js +81 -0
- package/dist-lib/spec/config/hyperschema/schema.json +19 -0
- package/dist-lib/spec/hrpc/hrpc.json +1500 -0
- package/dist-lib/spec/hrpc/index.d.ts +3 -0
- package/dist-lib/spec/hrpc/index.js +1545 -0
- package/dist-lib/spec/hrpc/messages.d.ts +4 -0
- package/dist-lib/spec/hrpc/messages.js +8373 -0
- package/dist-lib/spec/hyperschema/index.d.ts +20 -0
- package/dist-lib/spec/hyperschema/index.js +8373 -0
- package/dist-lib/spec/hyperschema/schema.json +6202 -0
- package/dist-lib/spec/hyperschema/types.d.ts +1545 -0
- package/dist-lib/spec/index.d.ts +36 -0
- package/dist-lib/spec/index.js +6 -0
- package/dist-lib/spec/local/hyperdb/db.json +186 -0
- package/dist-lib/spec/local/hyperdb/index.d.ts +9 -0
- package/dist-lib/spec/local/hyperdb/index.js +855 -0
- package/dist-lib/spec/local/hyperdb/messages.js +2189 -0
- package/dist-lib/spec/local/hyperschema/index.js +1208 -0
- package/dist-lib/spec/local/hyperschema/schema.json +639 -0
- package/dist-lib/spec/local/hyperschema/types.d.ts +159 -0
- package/dist-lib/spec/mesh/hyperdb/db.json +702 -0
- package/dist-lib/spec/mesh/hyperdb/index.d.ts +9 -0
- package/dist-lib/spec/mesh/hyperdb/index.js +2260 -0
- package/dist-lib/spec/mesh/hyperdb/messages.js +7453 -0
- package/dist-lib/spec/mesh/hyperdispatch/dispatch.json +174 -0
- package/dist-lib/spec/mesh/hyperdispatch/index.d.ts +13 -0
- package/dist-lib/spec/mesh/hyperdispatch/index.js +566 -0
- package/dist-lib/spec/mesh/hyperdispatch/messages.js +5713 -0
- package/dist-lib/spec/mesh/hyperschema/index.d.ts +14 -0
- package/dist-lib/spec/mesh/hyperschema/index.js +5713 -0
- package/dist-lib/spec/mesh/hyperschema/schema.json +3142 -0
- package/dist-lib/spec/mesh/hyperschema/types.d.ts +754 -0
- package/ducks.png +0 -0
- package/package.json +336 -1
package/README.md
ADDED
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="ducks.png" alt="" width="200">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">@qvac/core</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<b>Peer-to-peer AI across your devices</b>
|
|
9
|
+
<br>
|
|
10
|
+
Run on mobile, desktop or remotely via P2P magic
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
delegated inference · replicated agents, chats and skills · approve or
|
|
15
|
+
stop from anywhere · works offline · no servers
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="#integrating-a-harness">Harness</a> ·
|
|
20
|
+
<a href="#what-you-get-for-free">What you get</a> ·
|
|
21
|
+
<a href="#one-shot-tasks">Tasks</a> ·
|
|
22
|
+
<a href="#rendering-the-chunk-stream-any-ui">Chunk stream</a> ·
|
|
23
|
+
<a href="#harness-event-reference">Events</a> ·
|
|
24
|
+
<a href="#development">Development</a>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
A P2P, multi-device AI workbench core. Three pieces, one duplex stream:
|
|
28
|
+
|
|
29
|
+
- **`Core`** — the state authority. Owns the replicated mesh (agents, devices,
|
|
30
|
+
the chunk stream) and the device-local store, serves every capability over
|
|
31
|
+
hrpc. It contains **no AI whatsoever**.
|
|
32
|
+
- **`Client`** — the client. A thin proxy whose methods are generated from the
|
|
33
|
+
schema; everything an app (or an assistant) does goes through it.
|
|
34
|
+
- **`QvacAssistant`** — the execution host. Owns _your_ AI harness and drives
|
|
35
|
+
it purely through the Client's capability surface, exactly like any other
|
|
36
|
+
client. This is the only place inference happens.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
┌───────────── host app process ─────────────┐ ┌── sidecar / same process ──┐
|
|
40
|
+
│ UI ──► Client ◄── QvacAssistant ◄─ Harness│ ◄──► │ Core ◄──► mesh (other devices)
|
|
41
|
+
└────────────────────────────────────────────┘ └────────────────────────────┘
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Integrating a harness
|
|
45
|
+
|
|
46
|
+
A harness is one method. Implement `completion(context)` as an async iterable
|
|
47
|
+
of plain events and you are done — no knowledge of chunks, meshes, or RPC:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const harness = {
|
|
51
|
+
async *completion({ model, agent, messages, approve, signal }) {
|
|
52
|
+
// Call any model you like — llama.cpp, an HTTP API, a test fake…
|
|
53
|
+
const reply = await myModel.chat({ system: agent.bio, messages })
|
|
54
|
+
yield { type: 'content', text: reply }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Wire it up — prod shape, Core in its own Bare sidecar process:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { QvacAssistant, AssistantClient } from 'qvac-core'
|
|
63
|
+
|
|
64
|
+
// Spawns the bundled Core sidecar for free — ready() boots it, close()
|
|
65
|
+
// tears it down. Joining a mesh: pass meshSeed/meshKey from a decoded invite.
|
|
66
|
+
const engine = new AssistantClient({
|
|
67
|
+
storagePath: '/path/to/state',
|
|
68
|
+
device: { name: 'my-device', capabilityProfile: 'mid', recommendedModelName: 'Qwen3.5-9B' }
|
|
69
|
+
})
|
|
70
|
+
await engine.ready()
|
|
71
|
+
|
|
72
|
+
const assistant = new QvacAssistant(engine, harness)
|
|
73
|
+
await assistant.ready()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
No `Core` construction host-side at all — the sidecar entry boots it, your
|
|
77
|
+
process keeps only the Client, the assistant, and the model runtime. Pass
|
|
78
|
+
`entry` in the options to replace the bundled Core entry with your own.
|
|
79
|
+
|
|
80
|
+
For tests or a single-process host, `sidecar: false` boots the same Core
|
|
81
|
+
in-process from the same options — nothing else changes:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
const engine = new AssistantClient({ sidecar: false, storagePath, device })
|
|
85
|
+
await engine.ready() // engine.core is the in-process Core (null in sidecar mode)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
(Lower still, `Core`/`Client` construct directly over any duplex pair —
|
|
89
|
+
that's all either mode does.)
|
|
90
|
+
|
|
91
|
+
Either way that's the whole integration — the assistant can't tell the
|
|
92
|
+
difference, because it only ever speaks capabilities. It is a
|
|
93
|
+
`ready-resource`: `ready()`, `close()`, `suspend()`/`resume()` (mobile
|
|
94
|
+
backgrounding — stops taking new work; in-flight runs finish).
|
|
95
|
+
|
|
96
|
+
The real llama.cpp harness lives in `../assistant-harness` and implements this
|
|
97
|
+
same contract — see `tui/index.mjs` for it running end to end.
|
|
98
|
+
|
|
99
|
+
### The harness over a wire (desktop / mobile)
|
|
100
|
+
|
|
101
|
+
In-process is for tests and simple hosts. Real apps put the harness where
|
|
102
|
+
the model runtime belongs, behind the `qvac-harness` wire contract
|
|
103
|
+
— the same `Harness` object served on a pipe, and a `RemoteHarness` handle
|
|
104
|
+
the assistant takes unchanged. The rule: **colocate the harness with
|
|
105
|
+
wherever the SDK runs in-process.**
|
|
106
|
+
|
|
107
|
+
**Desktop (Electron)** — the harness gets its own OS process, so a llama.cpp
|
|
108
|
+
crash or OOM kills the AI, never the mesh or the app. The whole local stack
|
|
109
|
+
is three lines — `spawnHarness()` spawns the bundled llama.cpp harness
|
|
110
|
+
sidecar and hands back the handle the assistant takes unchanged:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import { QvacAssistant, AssistantClient } from 'qvac-core'
|
|
114
|
+
import { spawnHarness } from 'qvac-harness'
|
|
115
|
+
|
|
116
|
+
const engine = new AssistantClient({ storagePath, device })
|
|
117
|
+
const harness = spawnHarness({ entry, skillsDir, sdkStorage, logger })
|
|
118
|
+
const assistant = new QvacAssistant(engine, harness)
|
|
119
|
+
await assistant.ready()
|
|
120
|
+
|
|
121
|
+
harness.on('status', (s) => log.warn('harness', s)) // running / died, lives
|
|
122
|
+
// shutdown: harness.close() kills the spawned process
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Custom tool sets: pass your own `entry` to `spawnHarness` (the bundled entry
|
|
126
|
+
is `qvac-harness/worker/harness-sidecar.mjs` — two lines of
|
|
127
|
+
`serveHarness(Bare.IPC, new Harness({ tools }))` to copy).
|
|
128
|
+
|
|
129
|
+
**Mobile (React Native + bare-kit)** — worklets share the app process, so a
|
|
130
|
+
separate harness worklet buys no isolation and costs a hop; the harness
|
|
131
|
+
lives in the Core worklet (or speaks to the SDK's own worklet over its
|
|
132
|
+
pipe). Same contract, an in-worklet duplex instead of a spawn:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
// inside the Core worklet: Core + harness + assistant, one process
|
|
136
|
+
import { QvacAssistant, duplexPair } from 'qvac-core'
|
|
137
|
+
import { Harness, serveHarness, connectHarness } from 'qvac-harness'
|
|
138
|
+
|
|
139
|
+
const [serverEnd, clientEnd] = duplexPair()
|
|
140
|
+
serveHarness(serverEnd, new Harness({ tools }))
|
|
141
|
+
new QvacAssistant(
|
|
142
|
+
client,
|
|
143
|
+
connectHarness(() => clientEnd)
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Death is death** (day one, like v1): a harness death fails the in-flight
|
|
148
|
+
run with a `HARNESS_DIED` reason and fails every later call fast — never a
|
|
149
|
+
hang. The `'status'` events and `harness.lives` are in-memory observability;
|
|
150
|
+
restart/recovery policy lands on that plumbing later. The wire contract also
|
|
151
|
+
carries `agent.id`, so a harness scopes per-agent tool config/credentials
|
|
152
|
+
locally (a work agent's gmail never leaks to a personal agent) — that config
|
|
153
|
+
never crosses the wire or touches the mesh. See `../harness-rpc/README.md`
|
|
154
|
+
for the contract itself.
|
|
155
|
+
|
|
156
|
+
## What you get for free
|
|
157
|
+
|
|
158
|
+
Because every part of a run is a replicated chunk, the assistant inherits the
|
|
159
|
+
whole multi-device story without any harness code:
|
|
160
|
+
|
|
161
|
+
- **Delegation** — agents live on the mesh with a `providerDeviceId`. Any
|
|
162
|
+
device asks (`engine.runAgent({ chatId, agentId })` appends a `run-request`
|
|
163
|
+
chunk); the device that _provides_ the agent executes; every chunk streams
|
|
164
|
+
back to everyone. A request made while the provider is offline runs when it
|
|
165
|
+
returns. Asking requires no harness at all.
|
|
166
|
+
- **Approval** — `await approve(call)` inside your harness surfaces as an
|
|
167
|
+
approval-request chunk on every device; ANY of them may answer
|
|
168
|
+
(`engine.resolveApproval`), first decision wins, and your promise resolves.
|
|
169
|
+
A run that dies with an unanswered request auto-denies it — no peer ever
|
|
170
|
+
sees a forever-pending ask. Tools marked `autoApprove` skip the gate.
|
|
171
|
+
- **Stop** — any device may stop any run: `engine.stopRun` writes the
|
|
172
|
+
terminal `canceled` run-status itself (no executor needed — it settles an
|
|
173
|
+
orphaned run too); an executing harness's `signal` fires when the status
|
|
174
|
+
replicates to it, and it writes nothing more.
|
|
175
|
+
- **Model progress** — emit `'model'` events (`loading`/`downloading`/`ready`)
|
|
176
|
+
from your harness (plain EventEmitter) and every device watching the run
|
|
177
|
+
sees load/download progress, coalesced.
|
|
178
|
+
- **Metrics** — yield one `{ type: 'metrics', ... }` event and it lands on the
|
|
179
|
+
completed run-status chunk, replicated.
|
|
180
|
+
- **Crash safety** — the first run-status chunk is the replicated claim
|
|
181
|
+
marker: a restarted assistant never re-executes a run that already started.
|
|
182
|
+
|
|
183
|
+
## One-shot tasks
|
|
184
|
+
|
|
185
|
+
A chat is a conversation; a task is one question with one answer. `runTask`
|
|
186
|
+
composes the append/run/watch flow into a single await — no streaming, no chunk
|
|
187
|
+
bookkeeping — and resolves once the run settles:
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
import { runTask } from 'qvac-core/task'
|
|
191
|
+
|
|
192
|
+
const result = await runTask(engine, { agentId: agent.id, prompt: 'Say hi' })
|
|
193
|
+
|
|
194
|
+
result.state // 'completed' | 'failed' | 'canceled'
|
|
195
|
+
result.text // the answer: content after the last tool result, trimmed
|
|
196
|
+
result.thinking // kept apart from the answer
|
|
197
|
+
result.toolCalls // [{ name, arguments }]
|
|
198
|
+
result.attachments // what the run produced
|
|
199
|
+
result.sources // RAG hits the run cited
|
|
200
|
+
result.reason // why, when state is not 'completed'
|
|
201
|
+
result.chunks // the run's raw chunks, for anything the above flattened
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
A run that calls a tool interleaves content around `tool-call` / `tool-result`,
|
|
205
|
+
so `text` is the content after the last tool result — the model narrating its
|
|
206
|
+
way there is in `chunks`, not in the answer.
|
|
207
|
+
|
|
208
|
+
It opens its own chat, titled from the prompt; pass `chatId` to continue an
|
|
209
|
+
existing one instead. A failed or canceled run resolves like any other — the
|
|
210
|
+
outcome is in `state`, so nothing hangs waiting for an answer that is not
|
|
211
|
+
coming.
|
|
212
|
+
|
|
213
|
+
`skill: 'presentations'` forces that skill into the turn. It rides the same
|
|
214
|
+
`@mention` channel a user types, so the harness inlines the skill's
|
|
215
|
+
instructions into the message rather than leaving the model to call the loader
|
|
216
|
+
— and it overrides the agent's `enabledSkills`, which readiness alone gates.
|
|
217
|
+
One skill per task: the mention is the first token of the message.
|
|
218
|
+
|
|
219
|
+
## Rendering the chunk stream (any UI)
|
|
220
|
+
|
|
221
|
+
The engine exposes a snapshot chat catalog:
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
const { chats } = await engine.listChats()
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
It returns complete, active mesh chat rows ordered by `createdAt`, then `id` —
|
|
228
|
+
pure metadata, so `chatsWatch` only re-emits on create, rename, delete, or flag
|
|
229
|
+
changes. The existing replicated chat encoding uses zero as the default for an
|
|
230
|
+
absent optional `deletedAt`, so `deletedAt: 0` is not a representable tombstone
|
|
231
|
+
and is treated as active. Positive tombstone timestamps are excluded.
|
|
232
|
+
`listChats()` itself is a snapshot query. Chat create, update, delete, and watch
|
|
233
|
+
capabilities are available through the typed chat API.
|
|
234
|
+
|
|
235
|
+
### Chat activity
|
|
236
|
+
|
|
237
|
+
Derived per-chat run state rides its own lean stream so it never churns the
|
|
238
|
+
metadata list:
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
const { activities } = await engine.listChatActivity() // or engine.chatActivityWatch()
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Each row is just `{ id, activityState }` — a small state machine over the chat's
|
|
245
|
+
replicated runs:
|
|
246
|
+
|
|
247
|
+
- `queued` — a run requested but not yet executing.
|
|
248
|
+
- `running` — generic compute (thinking, responding, an ordinary tool call).
|
|
249
|
+
- `generating-image` / `generating-audio` / `transcribing-audio` — the active run
|
|
250
|
+
refined by the media it is producing.
|
|
251
|
+
- `awaiting-approval` — the active run paused on the user.
|
|
252
|
+
|
|
253
|
+
Every value is a mutually-exclusive state, not a parallel flag. Runs serialize
|
|
254
|
+
per chat, so at most one is executing. The list carries only active chats — a
|
|
255
|
+
chat absent from it is idle, so a busy list stays proportional to what's running
|
|
256
|
+
rather than to how many chats exist. Clients join these rows to `listChats` by
|
|
257
|
+
`id` and render the label per state, so the wire carries no words.
|
|
258
|
+
|
|
259
|
+
Pass `chatIds` to scope activity to a viewport: `chatActivityWatch({ chatIds })`
|
|
260
|
+
returns captions for only those chats, so a client watches what is on screen and
|
|
261
|
+
re-opens with a new window on scroll (chats it stops watching read as idle). An
|
|
262
|
+
empty or absent `chatIds` returns the whole active set.
|
|
263
|
+
|
|
264
|
+
The media states are the only phase detail on this stream, and they belong here
|
|
265
|
+
because they are coarse and boundary-driven — image generation is one long phase,
|
|
266
|
+
not a per-token flip. Core derives them from the run's `progress.phase`
|
|
267
|
+
(`generating` plus an unsettled `generate_music` → audio, otherwise
|
|
268
|
+
`generating` → image; `transcribing` → transcription) and its unsettled media
|
|
269
|
+
tool-calls (`generate_music` → audio before sampling starts).
|
|
270
|
+
Everything finer stays off the list: the moment-to-moment phase
|
|
271
|
+
(thinking/responding, which non-media tool, the live percentage) is on the
|
|
272
|
+
per-chat chunk stream the open chat subscribes to, and model download/load
|
|
273
|
+
progress stays in `modelDownloadsWatch` (it reads as generic `running` here). So
|
|
274
|
+
`chatActivityWatch` broadcasts only at these coarse boundaries, and `chatsWatch`
|
|
275
|
+
stays silent through a run entirely.
|
|
276
|
+
|
|
277
|
+
### Notifications
|
|
278
|
+
|
|
279
|
+
`notificationsWatch()` merges device-local model completions with chat-derived
|
|
280
|
+
`assistant-reply`, `run-failed`, `approval-required`, and `user-message` events.
|
|
281
|
+
The newest 200 events are returned by default, with `truncated` when more remain.
|
|
282
|
+
|
|
283
|
+
Chat rows carry `chatId`, `agentId`, `chunkId`, `seq`, and `createdAt`, with an
|
|
284
|
+
optional 280-character preview and agent name. Human messages also carry
|
|
285
|
+
`senderDeviceId`, optional `senderMemberId`, `senderName`, and `chatTitle`.
|
|
286
|
+
Messages from this device or another device of the same member are suppressed;
|
|
287
|
+
without membership the sender falls back to device identity. Deleted chats and
|
|
288
|
+
automatic diagram repairs stay silent.
|
|
289
|
+
|
|
290
|
+
`ackNotification({ chatId, seq })` advances the local cursor monotonically.
|
|
291
|
+
Pending approvals stay visible until resolved, including when answered elsewhere.
|
|
292
|
+
Model rows carry `kind: 'model-downloaded'`, `notificationId`, `modelName`, and
|
|
293
|
+
`createdAt`; `ackNotification({ notificationId })` acknowledges that completion
|
|
294
|
+
without touching a chat cursor. One latest completion per model survives restart,
|
|
295
|
+
and an old acknowledgement cannot clear a newer download.
|
|
296
|
+
|
|
297
|
+
The Core-lifetime stream follows mesh transitions through lifecycle events and
|
|
298
|
+
keeps local model events available without a mesh. It ends on Core shutdown or
|
|
299
|
+
subscriber cancellation. Clients own notification copy, foreground suppression,
|
|
300
|
+
and operating-system delivery.
|
|
301
|
+
|
|
302
|
+
The assistant persists a logical transfer identity and completion separately from
|
|
303
|
+
download progress. Retries and suspension retain that identity. Completion waits
|
|
304
|
+
for required companion assets; cached discovery and 100% progress do not notify.
|
|
305
|
+
Core also emits `notification` wake events for terminal run-status,
|
|
306
|
+
approval-request, and user-message chunks.
|
|
307
|
+
|
|
308
|
+
A chat IS its chunk stream — one watch drives the whole view, and every
|
|
309
|
+
device renders the same thing because they all see the same chunks:
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
for await (const { chunks } of engine.chunksWatch({ chatId })) render(chunks)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Each frame is the full chat in seq order. To render it, group chunks into
|
|
316
|
+
turns, then map each chunk type:
|
|
317
|
+
|
|
318
|
+
**Grouping.** A `user-message` (and an `attachment`) is its own user turn —
|
|
319
|
+
label it by `originDeviceId`, resolved to a name via `devicesWatch`. Every
|
|
320
|
+
chunk carrying a `runId` belongs to one assistant turn, anchored where its
|
|
321
|
+
`run-request` sits (that ordering matters: a message queued during a slow run
|
|
322
|
+
must sort before the reply that follows it).
|
|
323
|
+
|
|
324
|
+
**Per type:**
|
|
325
|
+
|
|
326
|
+
| chunk type | render as |
|
|
327
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
328
|
+
| `user-message` | the user's text (`chunk.userMessage.text`), labelled by device |
|
|
329
|
+
| `run-request` | nothing — it anchors the turn and marks "a run is in flight" before the provider even starts (`chunk.runRequest.agentId` names the agent) |
|
|
330
|
+
| `content` | assistant text (`chunk.content.text`); consecutive content chunks concatenate into one bubble |
|
|
331
|
+
| `thinking` | collapsed/dimmed reasoning segment (`chunk.thinking.text`) |
|
|
332
|
+
| `tool-call` | `→ name(arguments)` line from `chunk.toolCall` |
|
|
333
|
+
| `tool-result` | `← name: result` line from `chunk.toolResult` (collapse long results) |
|
|
334
|
+
| `attachment` | file card from `chunk.attachment` (`fileName`, `mimeType`, `byteLength`); fetch bytes with `engine.downloadAttachment({ blobId })` |
|
|
335
|
+
| `approval-request` | **the chunk IS the approval state**, in the typed `chunk.approvalRequest` struct. `status === 'pending'` → show the ask (prefer `resource` — e.g. `host wttr.in` — over raw arguments) with allow / always-allow / deny; any other status → a resolved badge |
|
|
336
|
+
| `run-status` | turn lifecycle, in the typed `chunk.runStatus` struct: `executing` with a `progress` sub-struct (`phase` `loading`/`downloading`, `model`, `percentage` — edited in place on the one executing chunk, labelled by the chunk's `originDeviceId`); `failed` → error with `reason`; `completed` → done, `metrics` (tokens, tok/s, ttft) |
|
|
337
|
+
|
|
338
|
+
**Approval answers** go through `engine.resolveApproval({ id, approved,
|
|
339
|
+
scope, expiresAt })` from ANY device — first decision wins, the flipped
|
|
340
|
+
chunk replicates to everyone. `scope: 'always'` also mints a standing
|
|
341
|
+
approval-rule from the request's tool + resource, so matching future calls
|
|
342
|
+
don't prompt anywhere until the rule expires — one hour by default; pass
|
|
343
|
+
`expiresAt` (ms since epoch) to grant longer or shorter
|
|
344
|
+
(`engine.listApprovalRules({})` shows what's remembered).
|
|
345
|
+
|
|
346
|
+
**Busy state** derives from the stream, not local flags: if the last
|
|
347
|
+
assistant turn has no terminal `run-status` (`completed`/`failed`/
|
|
348
|
+
`canceled`), a run is in flight somewhere — show the spinner on every device
|
|
349
|
+
and offer `engine.stopRun({ chatId, runId })`.
|
|
350
|
+
|
|
351
|
+
**Stopping** is a right of any device, not a request to the executor:
|
|
352
|
+
`stopRun` appends the terminal `canceled` run-status itself, so it settles a
|
|
353
|
+
run whether its provider is live, offline, or gone entirely (an orphaned
|
|
354
|
+
run's spinner). A live executor yields when the status replicates to it and
|
|
355
|
+
writes nothing more; if the run finished first, the stop is a no-op. The
|
|
356
|
+
first terminal status in seq order is always the run's one outcome.
|
|
357
|
+
|
|
358
|
+
**Orphaned agents.** Every agent from `listAgents`/`getAgent`/`agentsWatch`
|
|
359
|
+
carries a derived `orphaned` flag: its provider device row is gone or
|
|
360
|
+
revoked, so nobody can execute its runs until it's re-homed
|
|
361
|
+
(`updateAgent({ id, providerDeviceId })`). Render it as "waiting for
|
|
362
|
+
provider" — on the agent itself, and on a busy turn whose agent is orphaned
|
|
363
|
+
(that spinner will never resolve by itself). Device removal deliberately
|
|
364
|
+
does NOT delete the device's agents: their chats survive, and re-homing
|
|
365
|
+
brings them back.
|
|
366
|
+
|
|
367
|
+
`tui/app.mjs` (`turnsFromChunks`, `renderTurn`) is this table as
|
|
368
|
+
working code — the same model drives the TUI end to end.
|
|
369
|
+
|
|
370
|
+
## File status
|
|
371
|
+
|
|
372
|
+
`Mesh.files`, implemented in `lib/core/files/index.ts`, owns file access, local
|
|
373
|
+
processing state, and cleanup. Its internal modules expose one `index.ts` each:
|
|
374
|
+
Store handles bytes and ranges, Records handles the existing attachment and
|
|
375
|
+
knowledge surfaces, Processing owns worker lifetime, and RAG owns extraction,
|
|
376
|
+
artifacts, vectors, and retrieval. Removing a Knowledge entry preserves its
|
|
377
|
+
source while a live chat attachment still references it.
|
|
378
|
+
|
|
379
|
+
Browser clients keep using the pure `@qvac/core/rag/*` package exports; these
|
|
380
|
+
do not import the backend Files module.
|
|
381
|
+
|
|
382
|
+
`fileStatusWatch({ chatId })` returns a full snapshot of the chat's files, including
|
|
383
|
+
uploads still in progress. Each file has `fileId`, `state`, `readyForChat`, optional
|
|
384
|
+
`uploadProgress`, and `processors`. Each registered processor has an independent
|
|
385
|
+
queue, state and output identity. A processor outside a file's scope reports
|
|
386
|
+
`not-applicable`. Replacing the embedding model stops only RAG; other processors
|
|
387
|
+
continue for the lifetime of the mesh.
|
|
388
|
+
|
|
389
|
+
Source and output blobs download independently into local storage. An unavailable
|
|
390
|
+
file starts a background range download and yields its processing slot, so ready
|
|
391
|
+
files can proceed. Byte arrival wakes that file immediately, without waiting for
|
|
392
|
+
the retry timer or other downloads. These transfers have no 15-second cutoff;
|
|
393
|
+
deletion, suspension and shutdown cancel them. Each processor still limits its
|
|
394
|
+
CPU work to one job, and RAG production and repair share one embedding slot.
|
|
395
|
+
|
|
396
|
+
`Mesh.files` owns local processing, recovery, and readiness. Retrieval prioritizes
|
|
397
|
+
the current turn's attachments or named files, otherwise the chat's knowledge,
|
|
398
|
+
and waits up to five seconds total for indexing. A timeout leaves processing
|
|
399
|
+
running in the background for API searches. When a chat request attaches documents,
|
|
400
|
+
names pending files, or asks for a document overview, the reply then watches those
|
|
401
|
+
files until they are ready, failed or removed. It shows “Preparing documents…” and
|
|
402
|
+
refreshes retrieval before answering automatically; Stop closes that subscription.
|
|
403
|
+
Documents whose full text already fits the prompt need no index wait.
|
|
404
|
+
See the [cache and processing decision](docs/decisions/2026-07-15-vector-index-session-cache.md).
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
const statuses = engine.fileStatusWatch({ chatId })
|
|
408
|
+
statuses.on('data', ({ files }) => renderFileStates(files))
|
|
409
|
+
|
|
410
|
+
const upload = engine.uploadKnowledge()
|
|
411
|
+
upload.write({
|
|
412
|
+
chatId,
|
|
413
|
+
fileName: 'notes.txt',
|
|
414
|
+
mimeType: 'text/plain',
|
|
415
|
+
fileId,
|
|
416
|
+
totalBytes: bytes.length
|
|
417
|
+
})
|
|
418
|
+
upload.write({ data: bytes })
|
|
419
|
+
upload.end()
|
|
420
|
+
for await (const receipt of upload) {
|
|
421
|
+
if (receipt.knowledge) console.log(receipt.knowledge.id)
|
|
422
|
+
}
|
|
423
|
+
statuses.destroy()
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
The optional `fileId` must be unique and becomes the stored file ID. Omitting it
|
|
427
|
+
keeps server-generated IDs. Cancelled or failed uploads can reuse that ID once
|
|
428
|
+
the previous upload closes, provided no file was stored. Upload session details
|
|
429
|
+
expire one minute after closing; stored files remain visible.
|
|
430
|
+
Expiry stays private to the upload session. A supplied ID is reserved before
|
|
431
|
+
persisted-record validation, so concurrent uploads cannot acquire the same ID.
|
|
432
|
+
Both upload endpoints retain their existing progress
|
|
433
|
+
frames and final receipt. `uploadProgress.completed` counts original source bytes,
|
|
434
|
+
even when an image is normalized before storage. A missing or zero total means
|
|
435
|
+
unknown; do not calculate a percentage without a positive total.
|
|
436
|
+
|
|
437
|
+
Both endpoints share one upload path. A new Knowledge-panel document creates a
|
|
438
|
+
chat attachment and a knowledge row with the same ID and blob. Its receipt follows
|
|
439
|
+
both records, without waiting for indexing. The attachment belongs to the next
|
|
440
|
+
user message, allowing that question to read the document immediately. Removing
|
|
441
|
+
knowledge leaves the chat attachment downloadable until it is deleted too.
|
|
442
|
+
Direct `mesh.files.knowledge({ deviceId })` callers supply the uploader's device
|
|
443
|
+
identity; the Engine API supplies it without changing upload request frames.
|
|
444
|
+
|
|
445
|
+
Legacy panel-only rows can also be staged by their explicit ID, within their
|
|
446
|
+
original chat. Metadata reads need no blob bytes; unavailable bytes produce a
|
|
447
|
+
read error. This compatibility path writes no backfill or migration.
|
|
448
|
+
|
|
449
|
+
`fileRead({ chatId, fileId, metadataOnly? })` resolves either surface in the named
|
|
450
|
+
chat. The stream starts with `metadata`, then yields original bytes and cumulative
|
|
451
|
+
`bytesRead` / `byteLength`. A missing or deleted file returns an empty stream.
|
|
452
|
+
Metadata-only reads never open blob storage; destroying the stream cancels an
|
|
453
|
+
outstanding remote read. Assistant file staging uses this same endpoint.
|
|
454
|
+
|
|
455
|
+
Processor outputs reuse the existing artifact operation. Inline outputs are capped
|
|
456
|
+
at 512 KiB and carried inside the reference string, alongside a zero-length legacy
|
|
457
|
+
blob range. Their processor version has no RAG model separator, so released RAG
|
|
458
|
+
readers skip them. Inline data never enters blob storage or reclamation. The
|
|
459
|
+
compatibility suite runs released apply and reclaim code, including late joins,
|
|
460
|
+
reopen and downgrade, without changing mesh or local database schemas.
|
|
461
|
+
|
|
462
|
+
New image uploads opt into the `preview` processor. It produces an upright JPEG
|
|
463
|
+
from the first frame, at most 768 pixels on either side, targeting 80 KiB with
|
|
464
|
+
the 512 KiB inline ceiling. The uploader gets the first 30 seconds to produce it;
|
|
465
|
+
another capable peer can then recover missing work. Images uploaded before this
|
|
466
|
+
feature remain `not-applicable` and use their existing rendering path.
|
|
467
|
+
|
|
468
|
+
`filePreview({ chatId, fileId })` returns the preview's `outputId`, MIME type and
|
|
469
|
+
bytes, or an empty result while it is unavailable. It never downloads the original.
|
|
470
|
+
`fileRetry({ chatId, fileId, processorId })` clears local failure and retry state
|
|
471
|
+
for that processor. Desktop and mobile consume the same runtime file-status actor;
|
|
472
|
+
opening or saving an image requests its original separately. A failed preview has
|
|
473
|
+
retry and open-original actions.
|
|
474
|
+
|
|
475
|
+
Preview identity hashes the document ID, its immutable source core/range and the
|
|
476
|
+
processor version. Unlike RAG's source-byte hash, that source address lets a peer
|
|
477
|
+
recognize the matching preview without downloading the original. The version is
|
|
478
|
+
stored in the existing `embedderVersion` string as `processor:preview:1`, with zero
|
|
479
|
+
dimensions. Released devices ignore this output and continue reading originals;
|
|
480
|
+
no roster gate or forced update is needed for this compatible representation.
|
|
481
|
+
|
|
482
|
+
HTTP blob links probe the first requested block with a 15-second bound before
|
|
483
|
+
returning success headers. Missing blocks produce a transient HTTP failure; the
|
|
484
|
+
same URL can succeed after replication. HEAD and empty responses need no probe,
|
|
485
|
+
and byte ranges fetch only the requested range. Suspend and close cancel pending
|
|
486
|
+
probes. A deadline for stalls later in the response body remains follow-up work.
|
|
487
|
+
|
|
488
|
+
Progress is local to the uploading or processing device. Remote processing exposes
|
|
489
|
+
its existing replicated claim and result without invented counts. `claimedBy` is
|
|
490
|
+
the writer key; `agentId` is included when the current agent host matches it.
|
|
491
|
+
`published` means an output record exists, while `readyForChat` requires the local
|
|
492
|
+
RAG index to be loaded. Neither field promises that another device is ready.
|
|
493
|
+
|
|
494
|
+
Uploads fail after 30 seconds without an incoming frame. Cancelling releases their
|
|
495
|
+
writer and removes uncommitted bytes. Committed files survive cancellation that
|
|
496
|
+
arrives after publication. Suspend cancels outstanding source/output reads; resume
|
|
497
|
+
and peer connection allow recovery. An unavailable output can be rebuilt into the
|
|
498
|
+
local index when its source and embedding model are available, without changing
|
|
499
|
+
the immutable shared artifact reference. This local cache is rebuilt after restart.
|
|
500
|
+
|
|
501
|
+
`Files` owns the status consumed by both this watch and retrieval. `ragSearch`
|
|
502
|
+
includes `pendingFiles`, also surfaced in the prompt and source notice. Existing
|
|
503
|
+
knowledge and indexing APIs remain supported. `listIndexing` and `indexingWatch`
|
|
504
|
+
also report repeated source misses and locally unavailable outputs using the
|
|
505
|
+
existing response shape. These diagnostics never write shared status rows and
|
|
506
|
+
clear when the file becomes usable locally. This feature adds no replicated file
|
|
507
|
+
fields, operations, data migrations, or changes to existing RAG artifact IDs.
|
|
508
|
+
|
|
509
|
+
The TUI uses this watch for its live progress strip. `/files` lists the current
|
|
510
|
+
snapshot, including failures and whether each file is ready for chat on this
|
|
511
|
+
device. Upload with `/attach <path>` or `/knowledge add <path>`; `/knowledge`
|
|
512
|
+
retains its detailed indexing records for diagnostics.
|
|
513
|
+
|
|
514
|
+
## Watch streams: subscribe, unsubscribe, errors
|
|
515
|
+
|
|
516
|
+
Every `*Watch` capability (`chunksWatch`, `agentsWatch`, `devicesWatch`,
|
|
517
|
+
`configWatch`, `meshStatusWatch`) returns the same kind of stream, with one
|
|
518
|
+
contract:
|
|
519
|
+
|
|
520
|
+
- **The first frame is the current snapshot, immediately.** Subscribing IS
|
|
521
|
+
the fetch — there is no fetch-then-subscribe race to paper over, ever.
|
|
522
|
+
- **Later frames arrive only on real change** (identical snapshots are
|
|
523
|
+
deduped server-side), and each frame is the full current result, not a
|
|
524
|
+
delta.
|
|
525
|
+
- **`destroy()` is the unsubscribe** (ending a `for await` does the same
|
|
526
|
+
thing). Nothing else to call, no registry to clean up — in any process,
|
|
527
|
+
over any transport.
|
|
528
|
+
- **A dying stream can never crash the host.** Every stream comes pre-armed
|
|
529
|
+
with an error observer, so a consumer that only listens to `'data'` is
|
|
530
|
+
safe; `'close'` always fires, and a `for await` rejects with the reason.
|
|
531
|
+
|
|
532
|
+
The stream is typed end to end (`WatchStream<T>` — async-iterable AND
|
|
533
|
+
evented; `T` is the hyperschema frame type), so both idioms typecheck:
|
|
534
|
+
|
|
535
|
+
**Loop** — `break`/`return` unsubscribes:
|
|
536
|
+
|
|
537
|
+
```js
|
|
538
|
+
for await (const { agents } of engine.agentsWatch({})) {
|
|
539
|
+
render(agents)
|
|
540
|
+
if (done) break // break IS the unsubscribe
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Mount/unmount** — subscribe with `on('data')`, unsubscribe with
|
|
545
|
+
`destroy()`; this is plain streamx, nothing layered on top:
|
|
546
|
+
|
|
547
|
+
```js
|
|
548
|
+
const stream = engine.chunksWatch({ chatId }) // opening the chat
|
|
549
|
+
stream.on('data', ({ chunks }) => render(chunks))
|
|
550
|
+
// ...closing the chat:
|
|
551
|
+
stream.destroy() // the unsubscribe
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**One value, not a stream** — take the first frame and leave; still no
|
|
555
|
+
race, because frame one is the snapshot:
|
|
556
|
+
|
|
557
|
+
```js
|
|
558
|
+
async function once(stream) {
|
|
559
|
+
for await (const frame of stream) return frame // break-on-return unsubscribes
|
|
560
|
+
}
|
|
561
|
+
const { devices } = await once(engine.devicesWatch({}))
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
**Errors end the stream, they never hang it.** A server-side query failure
|
|
565
|
+
destroys the stream with that error (`for await` rejects with it); transport
|
|
566
|
+
death settles every open stream and pending call. So a long-lived consumer
|
|
567
|
+
is a re-arm loop, not error plumbing:
|
|
568
|
+
|
|
569
|
+
```js
|
|
570
|
+
while (!closing) {
|
|
571
|
+
try {
|
|
572
|
+
for await (const { chunks } of engine.chunksWatch({ chatId })) render(chunks)
|
|
573
|
+
break // server ended the stream deliberately
|
|
574
|
+
} catch {
|
|
575
|
+
await delay(1000) // transport blip — resubscribe (frame one re-syncs you)
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
Under the hood: ending the iterator destroys the wire stream, a DESTROY
|
|
581
|
+
frame crosses the transport, and the Core releases the underlying store
|
|
582
|
+
watcher — verified by test. The relays a host puts between the Client and
|
|
583
|
+
the Core (Electron IPC, a worklet bridge) carry these as ordinary bytes and
|
|
584
|
+
hold no subscription state, so there is nothing to unsubscribe at any layer
|
|
585
|
+
in between. If several consumers want the same data, share ONE subscription
|
|
586
|
+
and ref-count the unsubscribe in your app layer — the stream is cheap, but
|
|
587
|
+
one per consumer is still one store watcher each on the Core.
|
|
588
|
+
|
|
589
|
+
## Harness event reference
|
|
590
|
+
|
|
591
|
+
`completion()` may yield:
|
|
592
|
+
|
|
593
|
+
| event | shape | becomes |
|
|
594
|
+
| ------------- | -------------------------------------- | --------------------------------------------------------- |
|
|
595
|
+
| `content` | `{ type, text }` | `content` chunks (coalesced) |
|
|
596
|
+
| `thinking` | `{ type, text }` | `thinking` chunks |
|
|
597
|
+
| `tool-call` | `{ type, name, args }` | `tool-call` chunk (`toolCall.name`, `toolCall.arguments`) |
|
|
598
|
+
| `tool-result` | `{ type, name, result }` | `tool-result` chunk (`toolResult`) |
|
|
599
|
+
| `metrics` | `{ type, totalTokens, durationMs, … }` | stored on the completed run-status |
|
|
600
|
+
| `error` | `{ type, message }` | run-status `failed` |
|
|
601
|
+
| `aborted` | `{ type }` | run-status `canceled` |
|
|
602
|
+
|
|
603
|
+
Context you receive: `model` (the agent's model name), `agent`
|
|
604
|
+
(`{ name, bio }`), `messages` (`{ role, content }[]`), `approve(call)`,
|
|
605
|
+
`signal`.
|
|
606
|
+
|
|
607
|
+
## Connecting a skill (Notion, GitHub, …)
|
|
608
|
+
|
|
609
|
+
A skill reaches an external service through a **credential key** its
|
|
610
|
+
`SKILL.md` declares. The harness never sees the raw token: the credential
|
|
611
|
+
store resolves the key at call time and injects it as a bearer, and the
|
|
612
|
+
skill's `allow_list` binds that key to the URL prefixes it may be sent to.
|
|
613
|
+
Credentials are **per agent** (stored at `skills.<agentId>.<key>`), so two
|
|
614
|
+
agents never share one connection.
|
|
615
|
+
|
|
616
|
+
Fill a key with a slash command in the running TUI (`duck` drives the
|
|
617
|
+
command set):
|
|
618
|
+
|
|
619
|
+
- `/connect google` · `/connect spotify` · `/connect asana` · `/connect notion` — browser OAuth;
|
|
620
|
+
the example stores the token and refreshes it on use.
|
|
621
|
+
- `/cred <key> <value>` — set any credential key by hand, for a service with
|
|
622
|
+
no built-in OAuth flow.
|
|
623
|
+
|
|
624
|
+
`/connect google` needs `GOOGLE_OAUTH_CLIENT_ID` + `GOOGLE_OAUTH_CLIENT_SECRET`
|
|
625
|
+
in env, `/connect spotify` needs `SPOTIFY_OAUTH_CLIENT_ID`, `/connect asana` needs
|
|
626
|
+
`ASANA_OAUTH_CLIENT_ID` + `ASANA_OAUTH_CLIENT_SECRET` (an MCP-type app registered
|
|
627
|
+
at app.asana.com); `/connect notion` needs nothing — its MCP server self-provisions
|
|
628
|
+
a public client via dynamic registration (RFC 7591) + PKCE.
|
|
629
|
+
|
|
630
|
+
### Notion
|
|
631
|
+
|
|
632
|
+
Notion runs over its official MCP server (`https://mcp.notion.com/mcp`) via
|
|
633
|
+
the `mcp_call` tool, under the credential key `notion_mcp_access_token`:
|
|
634
|
+
|
|
635
|
+
```
|
|
636
|
+
/connect notion # authorize in the browser, pick the pages to share
|
|
637
|
+
/skill on notion # if the agent's skill scope is 'selected'
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
Then ask the assistant anything Notion — it loads the `notion` skill and calls
|
|
641
|
+
`mcp_call` itself; the session handshake and auth are handled for you. A `401`
|
|
642
|
+
in a tool result means the token expired — run `/connect notion` again.
|
|
643
|
+
|
|
644
|
+
```sh
|
|
645
|
+
duck
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
Office documents (`docx`, `doc`, `odt`) decode through an engine the host supplies rather
|
|
649
|
+
than a declared dependency: the package pulls all thirteen platform prebuilds at ~350MB
|
|
650
|
+
each, so declaring it would cost every install 4.3GB. Fetch the one for this machine before
|
|
651
|
+
exercising `/knowledge add report.docx`:
|
|
652
|
+
|
|
653
|
+
```sh
|
|
654
|
+
bun run engine:collabora
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
The TUI copies its mesh invite to the clipboard on ctrl+y. On a second machine
|
|
658
|
+
(or a second terminal):
|
|
659
|
+
|
|
660
|
+
```sh
|
|
661
|
+
duck qvac://mesh/…
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
A running TUI moves mesh with `/join qvac://mesh/…` — it retires the mesh it is
|
|
665
|
+
on, joins that one, and re-resolves the agent and chat from it.
|
|
666
|
+
|
|
667
|
+
`--headless` brings the same stack up with no TUI: it prints the invite and then
|
|
668
|
+
streams logs (including the sidecar child's own output, which the TUI drops) to
|
|
669
|
+
stdout until ctrl+C. That is the shape to run as a background mesh peer, and the
|
|
670
|
+
only way to read the engine's logs live — `QVAC_LOG_LEVEL` picks the level:
|
|
671
|
+
|
|
672
|
+
```sh
|
|
673
|
+
duck --headless
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
The joiner brings no models and no agents — it finds the mesh's seeded agent,
|
|
677
|
+
asks, and the _creator's_ device runs the model while both sides watch the
|
|
678
|
+
same replicated stream: load progress, thinking, tokens, tool approvals
|
|
679
|
+
(either side can answer), and final stats.
|
|
680
|
+
|
|
681
|
+
## Development
|
|
682
|
+
|
|
683
|
+
```sh
|
|
684
|
+
npm test # node + bare suites
|
|
685
|
+
npm run build:schema # regenerate spec/ from schema/ (bare)
|
|
686
|
+
bun run duck # build the TUI sidecar bundle, then run it
|
|
687
|
+
bun run duck:warm # run the TUI against the last-built bundle
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
The TUI lives in [`tui/`](tui/README.md) and installs as the `duck` command.
|
|
691
|
+
Schema changes go in `schema/*.ts` (builders) — `spec/` is generated. The
|
|
692
|
+
mesh schema (`spec/mesh/*`) is the replicated protocol: every device in a
|
|
693
|
+
mesh must run the same generated spec. See `MIGRATION.md` for the audited
|
|
694
|
+
feature checklist vs core v1.
|