@retinue/agentkit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +310 -0
- package/dist/adapters/bullmq/consumer.d.ts +33 -0
- package/dist/adapters/bullmq/consumer.js +41 -0
- package/dist/adapters/bullmq/dispatcher.d.ts +74 -0
- package/dist/adapters/bullmq/dispatcher.js +160 -0
- package/dist/adapters/bullmq/export.d.ts +31 -0
- package/dist/adapters/bullmq/export.js +53 -0
- package/dist/adapters/bullmq/extraction.d.ts +42 -0
- package/dist/adapters/bullmq/extraction.js +63 -0
- package/dist/adapters/bullmq/index.d.ts +13 -0
- package/dist/adapters/bullmq/index.js +13 -0
- package/dist/adapters/bullmq/lock.d.ts +77 -0
- package/dist/adapters/bullmq/lock.js +126 -0
- package/dist/adapters/bullmq/queue.d.ts +50 -0
- package/dist/adapters/bullmq/queue.js +81 -0
- package/dist/adapters/memory/artifact-exports.d.ts +11 -0
- package/dist/adapters/memory/artifact-exports.js +102 -0
- package/dist/adapters/memory/artifacts.d.ts +15 -0
- package/dist/adapters/memory/artifacts.js +134 -0
- package/dist/adapters/memory/blobs.d.ts +7 -0
- package/dist/adapters/memory/blobs.js +27 -0
- package/dist/adapters/memory/evaluation.d.ts +18 -0
- package/dist/adapters/memory/evaluation.js +148 -0
- package/dist/adapters/memory/files.d.ts +27 -0
- package/dist/adapters/memory/files.js +0 -0
- package/dist/adapters/memory/flows.d.ts +16 -0
- package/dist/adapters/memory/flows.js +117 -0
- package/dist/adapters/memory/hitl.d.ts +9 -0
- package/dist/adapters/memory/hitl.js +130 -0
- package/dist/adapters/memory/idempotency.d.ts +13 -0
- package/dist/adapters/memory/idempotency.js +32 -0
- package/dist/adapters/memory/index.d.ts +39 -0
- package/dist/adapters/memory/index.js +107 -0
- package/dist/adapters/memory/knowledge.d.ts +43 -0
- package/dist/adapters/memory/knowledge.js +248 -0
- package/dist/adapters/memory/mcp.d.ts +9 -0
- package/dist/adapters/memory/mcp.js +37 -0
- package/dist/adapters/memory/message-store.d.ts +17 -0
- package/dist/adapters/memory/message-store.js +70 -0
- package/dist/adapters/memory/principal-memory.d.ts +7 -0
- package/dist/adapters/memory/principal-memory.js +83 -0
- package/dist/adapters/memory/runtime.d.ts +29 -0
- package/dist/adapters/memory/runtime.js +0 -0
- package/dist/adapters/memory/sessions.d.ts +29 -0
- package/dist/adapters/memory/sessions.js +0 -0
- package/dist/adapters/memory/skills.d.ts +10 -0
- package/dist/adapters/memory/skills.js +41 -0
- package/dist/adapters/memory/thread-summary.d.ts +7 -0
- package/dist/adapters/memory/thread-summary.js +29 -0
- package/dist/adapters/memory/usage-limits.d.ts +13 -0
- package/dist/adapters/memory/usage-limits.js +72 -0
- package/dist/adapters/memory/usage.d.ts +16 -0
- package/dist/adapters/memory/usage.js +279 -0
- package/dist/adapters/otel/index.d.ts +111 -0
- package/dist/adapters/otel/index.js +133 -0
- package/dist/adapters/postgres/artifact-exports.d.ts +12 -0
- package/dist/adapters/postgres/artifact-exports.js +117 -0
- package/dist/adapters/postgres/artifacts.d.ts +16 -0
- package/dist/adapters/postgres/artifacts.js +172 -0
- package/dist/adapters/postgres/checkpoint-store.d.ts +16 -0
- package/dist/adapters/postgres/checkpoint-store.js +34 -0
- package/dist/adapters/postgres/config.d.ts +15 -0
- package/dist/adapters/postgres/config.js +187 -0
- package/dist/adapters/postgres/conversation-store.d.ts +4 -0
- package/dist/adapters/postgres/conversation-store.js +82 -0
- package/dist/adapters/postgres/evaluation.d.ts +17 -0
- package/dist/adapters/postgres/evaluation.js +193 -0
- package/dist/adapters/postgres/file-content.d.ts +30 -0
- package/dist/adapters/postgres/file-content.js +111 -0
- package/dist/adapters/postgres/files.d.ts +19 -0
- package/dist/adapters/postgres/files.js +209 -0
- package/dist/adapters/postgres/flows.d.ts +20 -0
- package/dist/adapters/postgres/flows.js +206 -0
- package/dist/adapters/postgres/hitl.d.ts +5 -0
- package/dist/adapters/postgres/hitl.js +247 -0
- package/dist/adapters/postgres/index.d.ts +35 -0
- package/dist/adapters/postgres/index.js +35 -0
- package/dist/adapters/postgres/knowledge.d.ts +48 -0
- package/dist/adapters/postgres/knowledge.js +255 -0
- package/dist/adapters/postgres/memory.d.ts +14 -0
- package/dist/adapters/postgres/memory.js +194 -0
- package/dist/adapters/postgres/message-store.d.ts +11 -0
- package/dist/adapters/postgres/message-store.js +145 -0
- package/dist/adapters/postgres/migrations.d.ts +69 -0
- package/dist/adapters/postgres/migrations.js +1594 -0
- package/dist/adapters/postgres/pg-executor.d.ts +19 -0
- package/dist/adapters/postgres/pg-executor.js +32 -0
- package/dist/adapters/postgres/retention.d.ts +26 -0
- package/dist/adapters/postgres/retention.js +59 -0
- package/dist/adapters/postgres/rollups.d.ts +17 -0
- package/dist/adapters/postgres/rollups.js +267 -0
- package/dist/adapters/postgres/run-coordinator.d.ts +5 -0
- package/dist/adapters/postgres/run-coordinator.js +98 -0
- package/dist/adapters/postgres/run-event-log.d.ts +26 -0
- package/dist/adapters/postgres/run-event-log.js +30 -0
- package/dist/adapters/postgres/run-store.d.ts +4 -0
- package/dist/adapters/postgres/run-store.js +199 -0
- package/dist/adapters/postgres/schema.d.ts +39 -0
- package/dist/adapters/postgres/schema.js +70 -0
- package/dist/adapters/postgres/session-state.d.ts +7 -0
- package/dist/adapters/postgres/session-state.js +99 -0
- package/dist/adapters/postgres/sql.d.ts +8 -0
- package/dist/adapters/postgres/sql.js +2 -0
- package/dist/adapters/postgres/transaction.d.ts +37 -0
- package/dist/adapters/postgres/transaction.js +93 -0
- package/dist/adapters/postgres/unit-of-work.d.ts +18 -0
- package/dist/adapters/postgres/unit-of-work.js +8 -0
- package/dist/adapters/postgres/usage-limits.d.ts +15 -0
- package/dist/adapters/postgres/usage-limits.js +136 -0
- package/dist/adapters/postgres/usage.d.ts +15 -0
- package/dist/adapters/postgres/usage.js +226 -0
- package/dist/adapters/redis/index.d.ts +9 -0
- package/dist/adapters/redis/index.js +9 -0
- package/dist/adapters/redis/realtime.d.ts +74 -0
- package/dist/adapters/redis/realtime.js +112 -0
- package/dist/adapters/supabase/index.d.ts +88 -0
- package/dist/adapters/supabase/index.js +84 -0
- package/dist/adapters/supabase/realtime.d.ts +30 -0
- package/dist/adapters/supabase/realtime.js +53 -0
- package/dist/adapters/supabase/rls.d.ts +99 -0
- package/dist/adapters/supabase/rls.js +216 -0
- package/dist/adapters/supabase/storage.d.ts +50 -0
- package/dist/adapters/supabase/storage.js +207 -0
- package/dist/agents/agent.d.ts +66 -0
- package/dist/agents/agent.js +209 -0
- package/dist/agents/define.d.ts +21 -0
- package/dist/agents/define.js +63 -0
- package/dist/agents/engine.d.ts +98 -0
- package/dist/agents/engine.js +462 -0
- package/dist/agents/index.d.ts +50 -0
- package/dist/agents/index.js +17 -0
- package/dist/artifacts/index.d.ts +114 -0
- package/dist/artifacts/index.js +219 -0
- package/dist/authorization/index.d.ts +76 -0
- package/dist/authorization/index.js +76 -0
- package/dist/capabilities/index.d.ts +120 -0
- package/dist/capabilities/index.js +167 -0
- package/dist/capabilities/runtime.d.ts +89 -0
- package/dist/capabilities/runtime.js +84 -0
- package/dist/citations/index.d.ts +161 -0
- package/dist/citations/index.js +182 -0
- package/dist/context/assembler.d.ts +82 -0
- package/dist/context/assembler.js +129 -0
- package/dist/context/compaction.d.ts +45 -0
- package/dist/context/compaction.js +55 -0
- package/dist/context/index.d.ts +75 -0
- package/dist/context/index.js +17 -0
- package/dist/core/content-parts.d.ts +194 -0
- package/dist/core/content-parts.js +23 -0
- package/dist/core/context.d.ts +51 -0
- package/dist/core/context.js +9 -0
- package/dist/core/env.d.ts +25 -0
- package/dist/core/env.js +41 -0
- package/dist/core/errors.d.ts +30 -0
- package/dist/core/errors.js +65 -0
- package/dist/core/events.d.ts +139 -0
- package/dist/core/events.js +99 -0
- package/dist/core/ids.d.ts +52 -0
- package/dist/core/ids.js +13 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +9 -0
- package/dist/core/tokens.d.ts +22 -0
- package/dist/core/tokens.js +22 -0
- package/dist/core/validation.d.ts +34 -0
- package/dist/core/validation.js +176 -0
- package/dist/documents/extraction.d.ts +121 -0
- package/dist/documents/extraction.js +293 -0
- package/dist/documents/index.d.ts +199 -0
- package/dist/documents/index.js +65 -0
- package/dist/documents/parsers/pdf.d.ts +47 -0
- package/dist/documents/parsers/pdf.js +508 -0
- package/dist/documents/parsers/text.d.ts +59 -0
- package/dist/documents/parsers/text.js +325 -0
- package/dist/documents/read-tool.d.ts +52 -0
- package/dist/documents/read-tool.js +109 -0
- package/dist/documents/render.d.ts +29 -0
- package/dist/documents/render.js +59 -0
- package/dist/documents/vision.d.ts +159 -0
- package/dist/documents/vision.js +214 -0
- package/dist/entries/adapters-bullmq.d.ts +8 -0
- package/dist/entries/adapters-bullmq.js +8 -0
- package/dist/entries/adapters-otel.d.ts +13 -0
- package/dist/entries/adapters-otel.js +13 -0
- package/dist/entries/adapters-postgres.d.ts +10 -0
- package/dist/entries/adapters-postgres.js +10 -0
- package/dist/entries/adapters-redis.d.ts +3 -0
- package/dist/entries/adapters-redis.js +3 -0
- package/dist/entries/context.d.ts +20 -0
- package/dist/entries/context.js +20 -0
- package/dist/entries/flows.d.ts +15 -0
- package/dist/entries/flows.js +15 -0
- package/dist/entries/hitl.d.ts +10 -0
- package/dist/entries/hitl.js +10 -0
- package/dist/entries/knowledge.d.ts +18 -0
- package/dist/entries/knowledge.js +19 -0
- package/dist/entries/mcp.d.ts +10 -0
- package/dist/entries/mcp.js +10 -0
- package/dist/entries/observability.d.ts +14 -0
- package/dist/entries/observability.js +16 -0
- package/dist/entries/persistence.d.ts +11 -0
- package/dist/entries/persistence.js +11 -0
- package/dist/entries/providers.d.ts +14 -0
- package/dist/entries/providers.js +14 -0
- package/dist/entries/runtime.d.ts +13 -0
- package/dist/entries/runtime.js +13 -0
- package/dist/entries/server.d.ts +24 -0
- package/dist/entries/server.js +24 -0
- package/dist/entries/tools.d.ts +21 -0
- package/dist/entries/tools.js +21 -0
- package/dist/entries/usage.d.ts +10 -0
- package/dist/entries/usage.js +10 -0
- package/dist/evaluation/gate.d.ts +168 -0
- package/dist/evaluation/gate.js +180 -0
- package/dist/evaluation/graders.d.ts +125 -0
- package/dist/evaluation/graders.js +203 -0
- package/dist/evaluation/index.d.ts +120 -0
- package/dist/evaluation/index.js +183 -0
- package/dist/evaluation/judge.d.ts +75 -0
- package/dist/evaluation/judge.js +111 -0
- package/dist/export/index.d.ts +162 -0
- package/dist/export/index.js +363 -0
- package/dist/export/markdown.d.ts +19 -0
- package/dist/export/markdown.js +29 -0
- package/dist/export/pdf.d.ts +73 -0
- package/dist/export/pdf.js +407 -0
- package/dist/files/context.d.ts +97 -0
- package/dist/files/context.js +185 -0
- package/dist/files/index.d.ts +210 -0
- package/dist/files/index.js +338 -0
- package/dist/files/read-tool.d.ts +81 -0
- package/dist/files/read-tool.js +163 -0
- package/dist/files/turn-parts.d.ts +96 -0
- package/dist/files/turn-parts.js +171 -0
- package/dist/flows/index.d.ts +270 -0
- package/dist/flows/index.js +62 -0
- package/dist/flows/interpreter.d.ts +146 -0
- package/dist/flows/interpreter.js +426 -0
- package/dist/flows/runner.d.ts +145 -0
- package/dist/flows/runner.js +270 -0
- package/dist/graphql/index.d.ts +8 -0
- package/dist/graphql/index.js +8 -0
- package/dist/graphql/resolvers.d.ts +237 -0
- package/dist/graphql/resolvers.js +253 -0
- package/dist/graphql/schema.d.ts +11 -0
- package/dist/graphql/schema.js +258 -0
- package/dist/graphql/sse.d.ts +77 -0
- package/dist/graphql/sse.js +100 -0
- package/dist/hitl/approved-execution.d.ts +127 -0
- package/dist/hitl/approved-execution.js +177 -0
- package/dist/hitl/index.d.ts +79 -0
- package/dist/hitl/index.js +12 -0
- package/dist/hitl/service.d.ts +221 -0
- package/dist/hitl/service.js +268 -0
- package/dist/idempotency/index.d.ts +70 -0
- package/dist/idempotency/index.js +59 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +60 -0
- package/dist/knowledge/chunking.d.ts +57 -0
- package/dist/knowledge/chunking.js +158 -0
- package/dist/knowledge/index.d.ts +119 -0
- package/dist/knowledge/index.js +166 -0
- package/dist/knowledge/retrieval.d.ts +146 -0
- package/dist/knowledge/retrieval.js +170 -0
- package/dist/loadtest/harness.d.ts +168 -0
- package/dist/loadtest/harness.js +507 -0
- package/dist/loadtest/index.d.ts +13 -0
- package/dist/loadtest/index.js +13 -0
- package/dist/loadtest/injection.d.ts +89 -0
- package/dist/loadtest/injection.js +147 -0
- package/dist/loadtest/metrics.d.ts +197 -0
- package/dist/loadtest/metrics.js +160 -0
- package/dist/loadtest/runbooks.d.ts +28 -0
- package/dist/loadtest/runbooks.js +159 -0
- package/dist/loadtest/scenario.d.ts +104 -0
- package/dist/loadtest/scenario.js +208 -0
- package/dist/mcp/egress.d.ts +53 -0
- package/dist/mcp/egress.js +115 -0
- package/dist/mcp/index.d.ts +93 -0
- package/dist/mcp/index.js +33 -0
- package/dist/mcp/provider.d.ts +62 -0
- package/dist/mcp/provider.js +0 -0
- package/dist/models/index.d.ts +98 -0
- package/dist/models/index.js +74 -0
- package/dist/models/pricing.d.ts +24 -0
- package/dist/models/pricing.js +37 -0
- package/dist/models/provider-factory.d.ts +31 -0
- package/dist/models/provider-factory.js +67 -0
- package/dist/models/streaming.d.ts +145 -0
- package/dist/models/streaming.js +272 -0
- package/dist/models/vision.d.ts +38 -0
- package/dist/models/vision.js +62 -0
- package/dist/persistence/index.d.ts +1654 -0
- package/dist/persistence/index.js +226 -0
- package/dist/principal-memory/index.d.ts +106 -0
- package/dist/principal-memory/index.js +89 -0
- package/dist/retention/index.d.ts +89 -0
- package/dist/retention/index.js +70 -0
- package/dist/runtime/checkpoint.d.ts +37 -0
- package/dist/runtime/checkpoint.js +22 -0
- package/dist/runtime/index.d.ts +118 -0
- package/dist/runtime/index.js +69 -0
- package/dist/runtime/retry.d.ts +95 -0
- package/dist/runtime/retry.js +126 -0
- package/dist/runtime/serialization.d.ts +85 -0
- package/dist/runtime/serialization.js +95 -0
- package/dist/runtime/streaming.d.ts +54 -0
- package/dist/runtime/streaming.js +115 -0
- package/dist/runtime/worker.d.ts +130 -0
- package/dist/runtime/worker.js +405 -0
- package/dist/security/checklist.d.ts +53 -0
- package/dist/security/checklist.js +204 -0
- package/dist/security/findings.d.ts +56 -0
- package/dist/security/findings.js +168 -0
- package/dist/security/index.d.ts +14 -0
- package/dist/security/index.js +14 -0
- package/dist/security/prompt-safety.d.ts +100 -0
- package/dist/security/prompt-safety.js +133 -0
- package/dist/server/boot.d.ts +32 -0
- package/dist/server/boot.js +36 -0
- package/dist/server/cli-worker.d.ts +37 -0
- package/dist/server/cli-worker.js +151 -0
- package/dist/server/cli.d.ts +27 -0
- package/dist/server/cli.js +74 -0
- package/dist/server/config.d.ts +42 -0
- package/dist/server/config.js +127 -0
- package/dist/server/health.d.ts +59 -0
- package/dist/server/health.js +90 -0
- package/dist/server/host.d.ts +39 -0
- package/dist/server/host.js +124 -0
- package/dist/server/index.d.ts +15 -0
- package/dist/server/index.js +15 -0
- package/dist/server/main.d.ts +16 -0
- package/dist/server/main.js +31 -0
- package/dist/server/sse-route.d.ts +21 -0
- package/dist/server/sse-route.js +282 -0
- package/dist/skills/index.d.ts +67 -0
- package/dist/skills/index.js +31 -0
- package/dist/skills/resolver.d.ts +54 -0
- package/dist/skills/resolver.js +121 -0
- package/dist/teams/index.d.ts +93 -0
- package/dist/teams/index.js +207 -0
- package/dist/telemetry/index.d.ts +157 -0
- package/dist/telemetry/index.js +71 -0
- package/dist/telemetry/instrument.d.ts +108 -0
- package/dist/telemetry/instrument.js +232 -0
- package/dist/telemetry/log-events.d.ts +17 -0
- package/dist/telemetry/log-events.js +58 -0
- package/dist/telemetry/metrics.d.ts +123 -0
- package/dist/telemetry/metrics.js +135 -0
- package/dist/telemetry/noop.d.ts +39 -0
- package/dist/telemetry/noop.js +143 -0
- package/dist/telemetry/redaction.d.ts +64 -0
- package/dist/telemetry/redaction.js +153 -0
- package/dist/telemetry/spans.d.ts +56 -0
- package/dist/telemetry/spans.js +78 -0
- package/dist/telemetry/trace-context.d.ts +55 -0
- package/dist/telemetry/trace-context.js +60 -0
- package/dist/toolkit/compute.d.ts +53 -0
- package/dist/toolkit/compute.js +152 -0
- package/dist/toolkit/data.d.ts +98 -0
- package/dist/toolkit/data.js +235 -0
- package/dist/toolkit/http.d.ts +113 -0
- package/dist/toolkit/http.js +205 -0
- package/dist/toolkit/index.d.ts +21 -0
- package/dist/toolkit/index.js +17 -0
- package/dist/toolkit/web.d.ts +107 -0
- package/dist/toolkit/web.js +147 -0
- package/dist/tools/define.d.ts +25 -0
- package/dist/tools/define.js +45 -0
- package/dist/tools/delegating.d.ts +132 -0
- package/dist/tools/delegating.js +211 -0
- package/dist/tools/index.d.ts +129 -0
- package/dist/tools/index.js +33 -0
- package/dist/tools/library/compute.d.ts +11 -0
- package/dist/tools/library/compute.js +46 -0
- package/dist/tools/library/data.d.ts +16 -0
- package/dist/tools/library/data.js +92 -0
- package/dist/tools/library/http.d.ts +28 -0
- package/dist/tools/library/http.js +71 -0
- package/dist/tools/library/index.d.ts +97 -0
- package/dist/tools/library/index.js +134 -0
- package/dist/tools/library/knowledge.d.ts +39 -0
- package/dist/tools/library/knowledge.js +58 -0
- package/dist/tools/library/web.d.ts +19 -0
- package/dist/tools/library/web.js +65 -0
- package/dist/tools/meta-tools.d.ts +19 -0
- package/dist/tools/meta-tools.js +36 -0
- package/dist/tools/registry.d.ts +146 -0
- package/dist/tools/registry.js +291 -0
- package/dist/usage/index.d.ts +105 -0
- package/dist/usage/index.js +20 -0
- package/dist/usage/quota.d.ts +258 -0
- package/dist/usage/quota.js +510 -0
- package/dist/usage/recorder.d.ts +29 -0
- package/dist/usage/recorder.js +96 -0
- package/dist/usage/rollups.d.ts +121 -0
- package/dist/usage/rollups.js +157 -0
- package/dist/worker/export.d.ts +57 -0
- package/dist/worker/export.js +81 -0
- package/dist/worker/extraction.d.ts +57 -0
- package/dist/worker/extraction.js +84 -0
- package/dist/worker/main.d.ts +103 -0
- package/dist/worker/main.js +159 -0
- package/package.json +187 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin GraphQL resolvers — `docs/06` → "Resolvers are thin: authenticate, validate, construct
|
|
3
|
+
* execution context and call platform services." The host's GraphQL server builds the execution
|
|
4
|
+
* context (from its auth) and puts it on the resolver context; every resolver here just validates
|
|
5
|
+
* shape and delegates to a service. No business logic lives in this layer.
|
|
6
|
+
*/
|
|
7
|
+
import { DEFAULT_WARN_AT, describeWindow } from "../usage/index.js";
|
|
8
|
+
import { asId } from "../core/ids.js";
|
|
9
|
+
import { startOrEnqueueRun } from "../runtime/index.js";
|
|
10
|
+
import { openRunEventStream } from "../runtime/index.js";
|
|
11
|
+
/** Serialize a stored Run to the GraphQL RunStatus enum (underscores, not hyphens). */
|
|
12
|
+
const runStatus = (status) => status.replace(/-/g, "_");
|
|
13
|
+
/**
|
|
14
|
+
* `conversationId` is nullable here since #198 — a run can belong to no conversation.
|
|
15
|
+
*
|
|
16
|
+
* Null, never a placeholder. A client that receives an empty string or a synthetic id would key a cache by it
|
|
17
|
+
* and group unrelated automations together.
|
|
18
|
+
*/
|
|
19
|
+
const toRun = (run) => ({
|
|
20
|
+
id: run.id,
|
|
21
|
+
conversationId: run.conversationId,
|
|
22
|
+
status: runStatus(run.status),
|
|
23
|
+
createdAt: run.createdAt,
|
|
24
|
+
finishedAt: run.finishedAt ?? null,
|
|
25
|
+
});
|
|
26
|
+
export const createResolvers = (deps) => {
|
|
27
|
+
const channelFor = deps.channelFor ?? ((id) => `conversation:${id}`);
|
|
28
|
+
const tid = (ctx) => ctx.execution.tenantId;
|
|
29
|
+
/**
|
|
30
|
+
* The quota state, from the guard rather than recomputed.
|
|
31
|
+
*
|
|
32
|
+
* Asking the guard means the panel's warning and the admission decision are the same computation. A UI that
|
|
33
|
+
* recomputed a threshold would eventually show "you are fine" while a run is being refused, which is worse
|
|
34
|
+
* than showing nothing.
|
|
35
|
+
*/
|
|
36
|
+
const quotaViewFor = async (guard, execution) => {
|
|
37
|
+
const decision = await guard.admit(execution);
|
|
38
|
+
const limits = await guard.limits(execution);
|
|
39
|
+
if (limits.length === 0)
|
|
40
|
+
return null;
|
|
41
|
+
/**
|
|
42
|
+
* The **binding** limit, which is the first — shortest span (#182).
|
|
43
|
+
*
|
|
44
|
+
* Several limits can apply at once and this view describes one, because that is the shape the schema has and
|
|
45
|
+
* a client rendering "you have used X of Y" wants the Y that will stop them soonest. `usageReport` is the
|
|
46
|
+
* wrong place to widen: a caller that wants all of them should ask for all of them.
|
|
47
|
+
*/
|
|
48
|
+
const binding = limits[0];
|
|
49
|
+
return {
|
|
50
|
+
window: describeWindow(binding.window),
|
|
51
|
+
// Null for a rolling window: no calendar period describes one, and naming the closest would be wrong
|
|
52
|
+
// rather than approximate.
|
|
53
|
+
period: binding.window.kind === "calendar" ? binding.window.period : null,
|
|
54
|
+
modelId: binding.modelId ?? null,
|
|
55
|
+
costLimitMinorUnits: binding.costMinorUnits ?? null,
|
|
56
|
+
inputTokenLimit: binding.inputTokens ?? null,
|
|
57
|
+
outputTokenLimit: binding.outputTokens ?? null,
|
|
58
|
+
warnAt: binding.warnAt ?? DEFAULT_WARN_AT,
|
|
59
|
+
warning: decision.admitted && decision.warnings.length > 0,
|
|
60
|
+
exceeded: !decision.admitted,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
Query: {
|
|
65
|
+
async conversations(_, args, ctx) {
|
|
66
|
+
return deps.conversations.list({ tenantId: tid(ctx), limit: args.limit, ...(args.cursor ? { cursor: args.cursor } : {}) });
|
|
67
|
+
},
|
|
68
|
+
async conversation(_, args, ctx) {
|
|
69
|
+
return deps.conversations.findById({ tenantId: tid(ctx), id: asId(args.id) });
|
|
70
|
+
},
|
|
71
|
+
async run(_, args, ctx) {
|
|
72
|
+
const run = await deps.runs.findById({ tenantId: tid(ctx), id: asId(args.id) });
|
|
73
|
+
return run ? toRun(run) : null;
|
|
74
|
+
},
|
|
75
|
+
async toolCatalog(_, args, ctx) {
|
|
76
|
+
return deps.toolRegistry.catalog(ctx.execution, args);
|
|
77
|
+
},
|
|
78
|
+
async usage(_, args, ctx) {
|
|
79
|
+
return deps.usage.totals({ tenantId: tid(ctx), ...(args.runId ? { runId: asId(args.runId) } : {}) });
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* The spend panel's one query (#140).
|
|
83
|
+
*
|
|
84
|
+
* Headline totals and buckets come from the **rollups**, so a page load never scans raw records however
|
|
85
|
+
* much a tenant has used. Breakdowns come from the ledger over the same bounded range — a deliberate
|
|
86
|
+
* trade documented on `UsageStore.breakdown`.
|
|
87
|
+
*
|
|
88
|
+
* Everything in one resolver so a panel cannot show a total from one moment and a breakdown from another;
|
|
89
|
+
* the discrepancy would look like a bug in the numbers rather than in the fetching.
|
|
90
|
+
*/
|
|
91
|
+
async usageReport(_, args, ctx) {
|
|
92
|
+
const period = args.period === "hour" ? "hour" : "day";
|
|
93
|
+
const limit = Math.min(Math.max(1, args.breakdownLimit ?? 10), 50);
|
|
94
|
+
const scope = { tenantId: tid(ctx), period, from: args.from, to: args.to };
|
|
95
|
+
// Absent rollup support is not an error: a deployment can run without the rollup job and still answer
|
|
96
|
+
// from the ledger. Reported as empty buckets rather than a failure, because a panel with no chart is
|
|
97
|
+
// usable and a panel with an error is not.
|
|
98
|
+
const buckets = deps.rollups === undefined ? { items: [] } : await deps.rollups.list({ ...scope, limit: 400 });
|
|
99
|
+
const totals = deps.rollups === undefined
|
|
100
|
+
? await deps.usage.totals({ tenantId: tid(ctx) })
|
|
101
|
+
: await deps.rollups.sum(scope);
|
|
102
|
+
const [byModel, byConversation] = await Promise.all([
|
|
103
|
+
deps.usage.breakdown({ tenantId: tid(ctx), from: args.from, to: args.to, by: "model", limit }),
|
|
104
|
+
deps.usage.breakdown({ tenantId: tid(ctx), from: args.from, to: args.to, by: "conversation", limit }),
|
|
105
|
+
]);
|
|
106
|
+
// The quota state as the *server* computes it, so a UI cannot disagree with the enforcement. Absent
|
|
107
|
+
// when no limit is configured, which means unbounded — a UI must show "no limit" rather than a full bar.
|
|
108
|
+
const quota = deps.quota === undefined ? null : await quotaViewFor(deps.quota, ctx.execution);
|
|
109
|
+
return {
|
|
110
|
+
period,
|
|
111
|
+
from: args.from,
|
|
112
|
+
to: args.to,
|
|
113
|
+
totals,
|
|
114
|
+
buckets: buckets.items.map((b) => ({
|
|
115
|
+
bucketStart: b.bucketStart,
|
|
116
|
+
currency: b.currency,
|
|
117
|
+
totals: b,
|
|
118
|
+
})),
|
|
119
|
+
byModel,
|
|
120
|
+
byConversation,
|
|
121
|
+
quota,
|
|
122
|
+
// The currency of the buckets in range. Empty when there is no usage — a period with no spend has no
|
|
123
|
+
// currency, and claiming one would be inventing a fact for a UI to format with.
|
|
124
|
+
currency: buckets.items.find((b) => b.currency !== "")?.currency ?? "",
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* #163. Shaped for rendering: the optional fields are filled in rather than passed through, because a
|
|
129
|
+
* client that has to treat `null` as "one choice" and `undefined` as "one choice" and `false` as "one
|
|
130
|
+
* choice" will eventually treat one of them as "several".
|
|
131
|
+
*/
|
|
132
|
+
async pendingQuestion(_, args, ctx) {
|
|
133
|
+
const question = await deps.questions.pending({ tenantId: tid(ctx), runId: asId(args.runId) });
|
|
134
|
+
if (question === null)
|
|
135
|
+
return null;
|
|
136
|
+
return {
|
|
137
|
+
interactionId: question.id,
|
|
138
|
+
runId: question.runId,
|
|
139
|
+
createdAt: question.createdAt,
|
|
140
|
+
questions: question.questions.map((q) => ({
|
|
141
|
+
key: q.key,
|
|
142
|
+
prompt: q.prompt,
|
|
143
|
+
options: q.options ?? [],
|
|
144
|
+
multiple: q.multiple === true,
|
|
145
|
+
// Free text with no options is the only sensible reading of a question that offers no choices, so
|
|
146
|
+
// it is implied there rather than left to each client to infer.
|
|
147
|
+
allowOther: q.allowOther === true || (q.options ?? []).length === 0,
|
|
148
|
+
})),
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
/** #163, the mirror of `pendingQuestion`. Wired the same way so the two cannot drift. */
|
|
152
|
+
async pendingApproval(_, args, ctx) {
|
|
153
|
+
const approval = await deps.approvals.pending({ tenantId: tid(ctx), runId: asId(args.runId) });
|
|
154
|
+
if (approval === null)
|
|
155
|
+
return null;
|
|
156
|
+
return {
|
|
157
|
+
interactionId: approval.id,
|
|
158
|
+
runId: approval.runId,
|
|
159
|
+
toolName: approval.toolName,
|
|
160
|
+
summary: approval.summary,
|
|
161
|
+
riskCategory: approval.riskCategory,
|
|
162
|
+
expiresAt: approval.expiresAt,
|
|
163
|
+
normalizedInput: approval.normalizedInput,
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
async conversationContext(_, args, ctx) {
|
|
167
|
+
if (!deps.inspectContext)
|
|
168
|
+
return null;
|
|
169
|
+
return deps.inspectContext(ctx.execution, {
|
|
170
|
+
conversationId: asId(args.conversationId),
|
|
171
|
+
...(args.runId ? { runId: asId(args.runId) } : {}),
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
Mutation: {
|
|
176
|
+
async createConversation(_, args, ctx) {
|
|
177
|
+
return deps.conversations.create({ tenantId: tid(ctx), id: asId(args.id), title: args.title });
|
|
178
|
+
},
|
|
179
|
+
async renameConversation(_, args, ctx) {
|
|
180
|
+
return deps.conversations.update({ tenantId: tid(ctx), id: asId(args.id), expectedVersion: args.expectedVersion, patch: { title: args.title } });
|
|
181
|
+
},
|
|
182
|
+
async archiveConversation(_, args, ctx) {
|
|
183
|
+
return deps.conversations.update({ tenantId: tid(ctx), id: asId(args.id), expectedVersion: args.expectedVersion, patch: { archivedAt: new Date().toISOString() } });
|
|
184
|
+
},
|
|
185
|
+
async deleteConversation(_, args, ctx) {
|
|
186
|
+
await deps.conversations.softDelete({ tenantId: tid(ctx), id: asId(args.id) });
|
|
187
|
+
return true;
|
|
188
|
+
},
|
|
189
|
+
async sendMessage(_, args, ctx) {
|
|
190
|
+
const conversationId = asId(args.conversationId);
|
|
191
|
+
const runId = asId(args.runId);
|
|
192
|
+
// #139, AC-2: the quota check is *here*, before the conversation is claimed and before anything is
|
|
193
|
+
// enqueued — so a refused run leaves no slot held, no job on the queue, and no partial answer. A limit
|
|
194
|
+
// enforced mid-run leaves a half-written response and a user who has to guess whether to retry.
|
|
195
|
+
//
|
|
196
|
+
// Optional, because a deployment with no limits configured is valid; when it is absent nothing is
|
|
197
|
+
// checked, which is the same as an unbounded limit.
|
|
198
|
+
if (deps.quota !== undefined)
|
|
199
|
+
await deps.quota.assertAdmitted(ctx.execution);
|
|
200
|
+
const started = await startOrEnqueueRun(deps.coordinator, { tenantId: tid(ctx), conversationId, runId });
|
|
201
|
+
if (started === "started")
|
|
202
|
+
await deps.dispatcher.enqueueRun({ tenantId: tid(ctx), runId });
|
|
203
|
+
const run = await deps.runs.findById({ tenantId: tid(ctx), id: runId });
|
|
204
|
+
return run ? toRun(run) : { id: runId, conversationId, status: started === "started" ? "running" : "queued", createdAt: new Date().toISOString(), finishedAt: null };
|
|
205
|
+
},
|
|
206
|
+
async cancelRun(_, args, ctx) {
|
|
207
|
+
await deps.runs.requestCancel({ tenantId: tid(ctx), id: asId(args.runId), now: new Date().toISOString() });
|
|
208
|
+
return true;
|
|
209
|
+
},
|
|
210
|
+
async answerQuestion(_, args, ctx) {
|
|
211
|
+
const { resumed } = await deps.questions.answer({
|
|
212
|
+
tenantId: tid(ctx),
|
|
213
|
+
interactionId: asId(args.input.interactionId),
|
|
214
|
+
runId: asId(args.input.runId),
|
|
215
|
+
answers: args.input.answers,
|
|
216
|
+
});
|
|
217
|
+
return resumed;
|
|
218
|
+
},
|
|
219
|
+
async decideApproval(_, args, ctx) {
|
|
220
|
+
const { resumed } = await deps.approvals.decide({
|
|
221
|
+
tenantId: tid(ctx),
|
|
222
|
+
interactionId: asId(args.input.interactionId),
|
|
223
|
+
runId: asId(args.input.runId),
|
|
224
|
+
decision: args.input.decision,
|
|
225
|
+
});
|
|
226
|
+
return resumed;
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
Subscription: {
|
|
230
|
+
runEvents: {
|
|
231
|
+
subscribe(_, args, ctx) {
|
|
232
|
+
return openRunEventStream({
|
|
233
|
+
tenantId: tid(ctx),
|
|
234
|
+
runId: asId(args.runId),
|
|
235
|
+
channel: channelFor(asId(args.conversationId)),
|
|
236
|
+
after: args.after ?? 0,
|
|
237
|
+
log: deps.eventLog,
|
|
238
|
+
live: deps.live,
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
// The transport delivers each event as { runEvents: { type, runId, sequence, occurredAt, payload } }.
|
|
242
|
+
resolve: (event) => ({
|
|
243
|
+
type: event.type,
|
|
244
|
+
runId: event.runId,
|
|
245
|
+
sequence: event.sequence,
|
|
246
|
+
occurredAt: event.occurredAt,
|
|
247
|
+
payload: event,
|
|
248
|
+
}),
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
//# sourceMappingURL=resolvers.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL schema (SDL) — `docs/06-graphql-and-frontend.md` → GraphQL boundary.
|
|
3
|
+
*
|
|
4
|
+
* The package ships the schema as SDL plus a thin resolver map (see `./resolvers`), so a host can
|
|
5
|
+
* mount it on any GraphQL server (Yoga, Apollo, Mercurius) without the library taking a server
|
|
6
|
+
* dependency. Resolvers stay thin: authenticate, validate, build the execution context, call a
|
|
7
|
+
* platform service. Subscriptions carry the stable `RunEvent` set and support resuming after a
|
|
8
|
+
* cursor via `openRunEventStream`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const typeDefs = "\n scalar JSON\n scalar DateTime\n\n type Conversation {\n id: ID!\n title: String!\n version: Int!\n archivedAt: DateTime\n createdAt: DateTime!\n updatedAt: DateTime!\n }\n\n type ConversationPage {\n items: [Conversation!]!\n nextCursor: String\n }\n\n enum RunStatus {\n queued\n running\n waiting_for_question\n waiting_for_approval\n retry_pending\n completed\n failed\n cancelled\n }\n\n type Run {\n id: ID!\n \"Null for a run that belongs to no conversation \u2014 a triggered automation rather than a chat turn (#198).\"\n conversationId: ID\n status: RunStatus!\n createdAt: DateTime!\n finishedAt: DateTime\n }\n\n type ToolCatalogEntry {\n name: String!\n label: String!\n description: String!\n category: String!\n effect: String!\n }\n\n type ToolCatalog {\n preloaded: [JSON!]!\n discoverable: [ToolCatalogEntry!]!\n meta: [ToolCatalogEntry!]!\n }\n\n type UsageTotals {\n inputTokens: Int!\n outputTokens: Int!\n cachedInputTokens: Int!\n costMinorUnits: Int!\n eventCount: Int!\n }\n\n \"One period's consumption. #139's rollups, so a chart never scans raw records.\"\n type UsageBucket {\n bucketStart: String!\n totals: UsageTotals!\n currency: String!\n }\n\n \"Consumption grouped by model or conversation over the requested range.\"\n type UsageBreakdownEntry {\n key: String!\n totals: UsageTotals!\n }\n\n \"\"\"\n Where the tenant stands against its limit.\n\n Absent when no limit is configured \u2014 which means unbounded, not zero. A UI must show \"no limit set\"\n rather than a full bar.\n \"\"\"\n type UsageQuota {\n \"\"\"\n The window in words \u2014 \"the day\", \"any 5 hours\" (#181).\n\n period stayed for the calendar case and is **null** for a rolling window, because no RollupPeriod describes\n one and returning \"hour\" for a five-hour window would be a wrong answer rather than a missing one. A client\n rendering the window reads this field; one keying a chart by bucket reads period and correctly finds nothing\n to key by.\n \"\"\"\n window: String!\n period: String\n \"The model this limit covers, or null for any model (#182).\"\n modelId: String\n costLimitMinorUnits: Int\n inputTokenLimit: Int\n outputTokenLimit: Int\n \"The fraction of the limit at which a warning shows. Sent so the UI cannot disagree with the server.\"\n warnAt: Float!\n \"True once any dimension is past warnAt and still admitted.\"\n warning: Boolean!\n \"True once any dimension has reached its limit, so work is being refused.\"\n exceeded: Boolean!\n }\n\n \"\"\"\n The usage report a spend panel renders.\n\n One query rather than several, so a panel cannot show a total from one moment and a breakdown from another.\n \"\"\"\n type UsageReport {\n period: String!\n from: String!\n to: String!\n totals: UsageTotals!\n buckets: [UsageBucket!]!\n byModel: [UsageBreakdownEntry!]!\n byConversation: [UsageBreakdownEntry!]!\n quota: UsageQuota\n currency: String!\n }\n\n \"A single transport event; payload carries the typed part / lifecycle detail.\"\n type RunEvent {\n type: String!\n runId: ID!\n sequence: Int!\n occurredAt: DateTime!\n payload: JSON!\n }\n\n \"\"\"\n One question put to a person, as it must be *rendered* \u2014 #163.\n\n The event that suspends a run carries only an interactionId, deliberately: events are thin, and a payload\n that duplicated the question would be a second copy to keep in step with the stored one. But nothing exposed\n the stored one either, so a client could answer a question it had no way to display. The example's picker\n rendered an empty text box next to \"The assistant has a question\", which is the whole gap in one screenshot.\n \"\"\"\n type PendingQuestionSpec {\n \"Stable key the answer is filed under.\"\n key: String!\n prompt: String!\n \"A short closed list, when there is one. Empty means free text.\"\n options: [String!]!\n \"Several choices are allowed, not one.\"\n multiple: Boolean!\n \"Free text is accepted alongside the options.\"\n allowOther: Boolean!\n }\n\n type PendingQuestion {\n interactionId: ID!\n runId: ID!\n questions: [PendingQuestionSpec!]!\n createdAt: DateTime!\n }\n\n \"\"\"\n The approval a run is parked on \u2014 the read side of decideApproval (#163).\n\n The same gap as pendingQuestion and milder rather than absent: approval.requested also carries only an\n interaction id, so a client had no summary to show and fell back to a generic \"Run a tool?\". Asking someone\n to authorise an action the card cannot name is how approval becomes a reflex.\n \"\"\"\n type PendingApprovalDetail {\n interactionId: ID!\n runId: ID!\n toolName: String!\n \"One line a person can decide on, written by the host's summarizer.\"\n summary: String!\n riskCategory: String!\n expiresAt: DateTime!\n \"The arguments the approval is for, so what runs is what was shown.\"\n normalizedInput: JSON!\n }\n\n input QuestionAnswerInput {\n interactionId: ID!\n runId: ID!\n answers: JSON!\n }\n\n input ApprovalDecisionInput {\n interactionId: ID!\n runId: ID!\n decision: String!\n }\n\n \"A section that shaped a turn's prompt \u2014 for the context inspector (#39).\"\n type InspectedSection {\n title: String!\n providerId: String!\n kind: String!\n provenance: String!\n estimatedTokens: Int!\n sensitivity: String!\n included: Boolean!\n prunedReason: String\n }\n\n type ContextInspection {\n sections: [InspectedSection!]!\n totalTokens: Int!\n budget: JSON!\n }\n\n type Query {\n conversations(limit: Int!, cursor: String): ConversationPage!\n conversation(id: ID!): Conversation\n run(id: ID!): Run\n toolCatalog(preloaded: [String!]!, categories: [String!]!, excluded: [String!]!): ToolCatalog!\n usage(runId: ID): UsageTotals!\n \"\"\"\n Consumption and cost by period, with breakdowns and quota state (#140).\n\n An extension of the usage query rather than a second endpoint: a panel showing a total from one query and a\n breakdown from another can show two moments at once, and the discrepancy looks like a bug in the numbers.\n \"\"\"\n usageReport(period: String!, from: String!, to: String!, breakdownLimit: Int): UsageReport!\n \"What context shaped a turn \u2014 attributes memory/tools/history that influenced the prompt.\"\n conversationContext(conversationId: ID!, runId: ID): ContextInspection\n \"\"\"\n The question a run is parked on, or null \u2014 the read side of answerQuestion (#163).\n\n Null covers both \"this run was never asked anything\" and \"it has been answered already\", because a client\n has the same thing to do in either case: show no picker. A run that is waiting is the only state with a\n question to render.\n \"\"\"\n pendingQuestion(runId: ID!): PendingQuestion\n \"The approval a run is parked on, or null (#163).\"\n pendingApproval(runId: ID!): PendingApprovalDetail\n }\n\n type Mutation {\n createConversation(id: ID!, title: String!): Conversation!\n renameConversation(id: ID!, expectedVersion: Int!, title: String!): Conversation!\n archiveConversation(id: ID!, expectedVersion: Int!): Conversation!\n deleteConversation(id: ID!): Boolean!\n sendMessage(conversationId: ID!, runId: ID!): Run!\n cancelRun(runId: ID!): Boolean!\n answerQuestion(input: QuestionAnswerInput!): Boolean!\n decideApproval(input: ApprovalDecisionInput!): Boolean!\n }\n\n type Subscription {\n \"Conversation/run events, resumable after a cursor (sequence).\"\n runEvents(runId: ID!, conversationId: ID!, after: Int): RunEvent!\n }\n";
|
|
11
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL schema (SDL) — `docs/06-graphql-and-frontend.md` → GraphQL boundary.
|
|
3
|
+
*
|
|
4
|
+
* The package ships the schema as SDL plus a thin resolver map (see `./resolvers`), so a host can
|
|
5
|
+
* mount it on any GraphQL server (Yoga, Apollo, Mercurius) without the library taking a server
|
|
6
|
+
* dependency. Resolvers stay thin: authenticate, validate, build the execution context, call a
|
|
7
|
+
* platform service. Subscriptions carry the stable `RunEvent` set and support resuming after a
|
|
8
|
+
* cursor via `openRunEventStream`.
|
|
9
|
+
*/
|
|
10
|
+
export const typeDefs = /* GraphQL */ `
|
|
11
|
+
scalar JSON
|
|
12
|
+
scalar DateTime
|
|
13
|
+
|
|
14
|
+
type Conversation {
|
|
15
|
+
id: ID!
|
|
16
|
+
title: String!
|
|
17
|
+
version: Int!
|
|
18
|
+
archivedAt: DateTime
|
|
19
|
+
createdAt: DateTime!
|
|
20
|
+
updatedAt: DateTime!
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type ConversationPage {
|
|
24
|
+
items: [Conversation!]!
|
|
25
|
+
nextCursor: String
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
enum RunStatus {
|
|
29
|
+
queued
|
|
30
|
+
running
|
|
31
|
+
waiting_for_question
|
|
32
|
+
waiting_for_approval
|
|
33
|
+
retry_pending
|
|
34
|
+
completed
|
|
35
|
+
failed
|
|
36
|
+
cancelled
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type Run {
|
|
40
|
+
id: ID!
|
|
41
|
+
"Null for a run that belongs to no conversation — a triggered automation rather than a chat turn (#198)."
|
|
42
|
+
conversationId: ID
|
|
43
|
+
status: RunStatus!
|
|
44
|
+
createdAt: DateTime!
|
|
45
|
+
finishedAt: DateTime
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type ToolCatalogEntry {
|
|
49
|
+
name: String!
|
|
50
|
+
label: String!
|
|
51
|
+
description: String!
|
|
52
|
+
category: String!
|
|
53
|
+
effect: String!
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type ToolCatalog {
|
|
57
|
+
preloaded: [JSON!]!
|
|
58
|
+
discoverable: [ToolCatalogEntry!]!
|
|
59
|
+
meta: [ToolCatalogEntry!]!
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type UsageTotals {
|
|
63
|
+
inputTokens: Int!
|
|
64
|
+
outputTokens: Int!
|
|
65
|
+
cachedInputTokens: Int!
|
|
66
|
+
costMinorUnits: Int!
|
|
67
|
+
eventCount: Int!
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
"One period's consumption. #139's rollups, so a chart never scans raw records."
|
|
71
|
+
type UsageBucket {
|
|
72
|
+
bucketStart: String!
|
|
73
|
+
totals: UsageTotals!
|
|
74
|
+
currency: String!
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
"Consumption grouped by model or conversation over the requested range."
|
|
78
|
+
type UsageBreakdownEntry {
|
|
79
|
+
key: String!
|
|
80
|
+
totals: UsageTotals!
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
Where the tenant stands against its limit.
|
|
85
|
+
|
|
86
|
+
Absent when no limit is configured — which means unbounded, not zero. A UI must show "no limit set"
|
|
87
|
+
rather than a full bar.
|
|
88
|
+
"""
|
|
89
|
+
type UsageQuota {
|
|
90
|
+
"""
|
|
91
|
+
The window in words — "the day", "any 5 hours" (#181).
|
|
92
|
+
|
|
93
|
+
period stayed for the calendar case and is **null** for a rolling window, because no RollupPeriod describes
|
|
94
|
+
one and returning "hour" for a five-hour window would be a wrong answer rather than a missing one. A client
|
|
95
|
+
rendering the window reads this field; one keying a chart by bucket reads period and correctly finds nothing
|
|
96
|
+
to key by.
|
|
97
|
+
"""
|
|
98
|
+
window: String!
|
|
99
|
+
period: String
|
|
100
|
+
"The model this limit covers, or null for any model (#182)."
|
|
101
|
+
modelId: String
|
|
102
|
+
costLimitMinorUnits: Int
|
|
103
|
+
inputTokenLimit: Int
|
|
104
|
+
outputTokenLimit: Int
|
|
105
|
+
"The fraction of the limit at which a warning shows. Sent so the UI cannot disagree with the server."
|
|
106
|
+
warnAt: Float!
|
|
107
|
+
"True once any dimension is past warnAt and still admitted."
|
|
108
|
+
warning: Boolean!
|
|
109
|
+
"True once any dimension has reached its limit, so work is being refused."
|
|
110
|
+
exceeded: Boolean!
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
The usage report a spend panel renders.
|
|
115
|
+
|
|
116
|
+
One query rather than several, so a panel cannot show a total from one moment and a breakdown from another.
|
|
117
|
+
"""
|
|
118
|
+
type UsageReport {
|
|
119
|
+
period: String!
|
|
120
|
+
from: String!
|
|
121
|
+
to: String!
|
|
122
|
+
totals: UsageTotals!
|
|
123
|
+
buckets: [UsageBucket!]!
|
|
124
|
+
byModel: [UsageBreakdownEntry!]!
|
|
125
|
+
byConversation: [UsageBreakdownEntry!]!
|
|
126
|
+
quota: UsageQuota
|
|
127
|
+
currency: String!
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
"A single transport event; payload carries the typed part / lifecycle detail."
|
|
131
|
+
type RunEvent {
|
|
132
|
+
type: String!
|
|
133
|
+
runId: ID!
|
|
134
|
+
sequence: Int!
|
|
135
|
+
occurredAt: DateTime!
|
|
136
|
+
payload: JSON!
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
One question put to a person, as it must be *rendered* — #163.
|
|
141
|
+
|
|
142
|
+
The event that suspends a run carries only an interactionId, deliberately: events are thin, and a payload
|
|
143
|
+
that duplicated the question would be a second copy to keep in step with the stored one. But nothing exposed
|
|
144
|
+
the stored one either, so a client could answer a question it had no way to display. The example's picker
|
|
145
|
+
rendered an empty text box next to "The assistant has a question", which is the whole gap in one screenshot.
|
|
146
|
+
"""
|
|
147
|
+
type PendingQuestionSpec {
|
|
148
|
+
"Stable key the answer is filed under."
|
|
149
|
+
key: String!
|
|
150
|
+
prompt: String!
|
|
151
|
+
"A short closed list, when there is one. Empty means free text."
|
|
152
|
+
options: [String!]!
|
|
153
|
+
"Several choices are allowed, not one."
|
|
154
|
+
multiple: Boolean!
|
|
155
|
+
"Free text is accepted alongside the options."
|
|
156
|
+
allowOther: Boolean!
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
type PendingQuestion {
|
|
160
|
+
interactionId: ID!
|
|
161
|
+
runId: ID!
|
|
162
|
+
questions: [PendingQuestionSpec!]!
|
|
163
|
+
createdAt: DateTime!
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
The approval a run is parked on — the read side of decideApproval (#163).
|
|
168
|
+
|
|
169
|
+
The same gap as pendingQuestion and milder rather than absent: approval.requested also carries only an
|
|
170
|
+
interaction id, so a client had no summary to show and fell back to a generic "Run a tool?". Asking someone
|
|
171
|
+
to authorise an action the card cannot name is how approval becomes a reflex.
|
|
172
|
+
"""
|
|
173
|
+
type PendingApprovalDetail {
|
|
174
|
+
interactionId: ID!
|
|
175
|
+
runId: ID!
|
|
176
|
+
toolName: String!
|
|
177
|
+
"One line a person can decide on, written by the host's summarizer."
|
|
178
|
+
summary: String!
|
|
179
|
+
riskCategory: String!
|
|
180
|
+
expiresAt: DateTime!
|
|
181
|
+
"The arguments the approval is for, so what runs is what was shown."
|
|
182
|
+
normalizedInput: JSON!
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
input QuestionAnswerInput {
|
|
186
|
+
interactionId: ID!
|
|
187
|
+
runId: ID!
|
|
188
|
+
answers: JSON!
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
input ApprovalDecisionInput {
|
|
192
|
+
interactionId: ID!
|
|
193
|
+
runId: ID!
|
|
194
|
+
decision: String!
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
"A section that shaped a turn's prompt — for the context inspector (#39)."
|
|
198
|
+
type InspectedSection {
|
|
199
|
+
title: String!
|
|
200
|
+
providerId: String!
|
|
201
|
+
kind: String!
|
|
202
|
+
provenance: String!
|
|
203
|
+
estimatedTokens: Int!
|
|
204
|
+
sensitivity: String!
|
|
205
|
+
included: Boolean!
|
|
206
|
+
prunedReason: String
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
type ContextInspection {
|
|
210
|
+
sections: [InspectedSection!]!
|
|
211
|
+
totalTokens: Int!
|
|
212
|
+
budget: JSON!
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
type Query {
|
|
216
|
+
conversations(limit: Int!, cursor: String): ConversationPage!
|
|
217
|
+
conversation(id: ID!): Conversation
|
|
218
|
+
run(id: ID!): Run
|
|
219
|
+
toolCatalog(preloaded: [String!]!, categories: [String!]!, excluded: [String!]!): ToolCatalog!
|
|
220
|
+
usage(runId: ID): UsageTotals!
|
|
221
|
+
"""
|
|
222
|
+
Consumption and cost by period, with breakdowns and quota state (#140).
|
|
223
|
+
|
|
224
|
+
An extension of the usage query rather than a second endpoint: a panel showing a total from one query and a
|
|
225
|
+
breakdown from another can show two moments at once, and the discrepancy looks like a bug in the numbers.
|
|
226
|
+
"""
|
|
227
|
+
usageReport(period: String!, from: String!, to: String!, breakdownLimit: Int): UsageReport!
|
|
228
|
+
"What context shaped a turn — attributes memory/tools/history that influenced the prompt."
|
|
229
|
+
conversationContext(conversationId: ID!, runId: ID): ContextInspection
|
|
230
|
+
"""
|
|
231
|
+
The question a run is parked on, or null — the read side of answerQuestion (#163).
|
|
232
|
+
|
|
233
|
+
Null covers both "this run was never asked anything" and "it has been answered already", because a client
|
|
234
|
+
has the same thing to do in either case: show no picker. A run that is waiting is the only state with a
|
|
235
|
+
question to render.
|
|
236
|
+
"""
|
|
237
|
+
pendingQuestion(runId: ID!): PendingQuestion
|
|
238
|
+
"The approval a run is parked on, or null (#163)."
|
|
239
|
+
pendingApproval(runId: ID!): PendingApprovalDetail
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
type Mutation {
|
|
243
|
+
createConversation(id: ID!, title: String!): Conversation!
|
|
244
|
+
renameConversation(id: ID!, expectedVersion: Int!, title: String!): Conversation!
|
|
245
|
+
archiveConversation(id: ID!, expectedVersion: Int!): Conversation!
|
|
246
|
+
deleteConversation(id: ID!): Boolean!
|
|
247
|
+
sendMessage(conversationId: ID!, runId: ID!): Run!
|
|
248
|
+
cancelRun(runId: ID!): Boolean!
|
|
249
|
+
answerQuestion(input: QuestionAnswerInput!): Boolean!
|
|
250
|
+
decideApproval(input: ApprovalDecisionInput!): Boolean!
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
type Subscription {
|
|
254
|
+
"Conversation/run events, resumable after a cursor (sequence)."
|
|
255
|
+
runEvents(runId: ID!, conversationId: ID!, after: Int): RunEvent!
|
|
256
|
+
}
|
|
257
|
+
`;
|
|
258
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE transport adapter — `docs/06` (embedded-profile streaming). A thin mapping from the
|
|
3
|
+
* transport-neutral `RunEvent` stream to Server-Sent Events frames. It takes no HTTP-framework
|
|
4
|
+
* dependency: `sseFrames` yields ready-to-write text frames, and `openRunEventSse` composes the
|
|
5
|
+
* reconnect stream (catch-up + live) with SSE encoding. The `Last-Event-ID` header a browser
|
|
6
|
+
* resends on reconnect maps directly to the run event `sequence`, so resuming loses no events.
|
|
7
|
+
*/
|
|
8
|
+
import type { RunEvent, RunEventLog } from "../core/events.js";
|
|
9
|
+
import type { ConversationId, RunId, TenantId } from "../core/ids.js";
|
|
10
|
+
import { type LiveEventSource } from "../runtime/index.js";
|
|
11
|
+
/**
|
|
12
|
+
* The GraphQL field the frames are shaped for.
|
|
13
|
+
*
|
|
14
|
+
* Hard-coded to match the SDL's `runEvents` subscription, because that is the selection a client made.
|
|
15
|
+
* A client that *aliased* the field would look for its alias and not find it — see the open question
|
|
16
|
+
* on #111; the adapter has no way to know the alias.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SSE_RESULT_FIELD = "runEvents";
|
|
19
|
+
/**
|
|
20
|
+
* Encode one event as a graphql-sse `next` frame.
|
|
21
|
+
*
|
|
22
|
+
* `docs/extraction/twenty-sdk-comparison.md` records the decision: *"Match the `graphql-sse` framing
|
|
23
|
+
* rather than inventing a bespoke SSE protocol"* — twenty-sdk already streams GraphQL over graphql-sse,
|
|
24
|
+
* so a client can consume this unmodified. This previously emitted `event: <RunEvent.type>` with a raw
|
|
25
|
+
* `RunEvent` as `data`, which no graphql-sse client understands.
|
|
26
|
+
*
|
|
27
|
+
* The `id:` line keeps carrying `RunEvent.sequence`, which is what makes `Last-Event-ID` resume work.
|
|
28
|
+
*
|
|
29
|
+
* **A failed run is a `next` frame, not an error frame**, and that is deliberate. `run.failed` is a
|
|
30
|
+
* durable event with a sequence, sitting in the log like any other. Delivering it as a protocol-level
|
|
31
|
+
* error would leave it with no `id:` — so `Last-Event-ID` could not resume past it, and a reconnecting
|
|
32
|
+
* client would never learn the run ended. An `ExecutionResult` may carry both `data` and `errors`, so
|
|
33
|
+
* the event travels in `data` and the platform error in `errors`: clients surface it through their
|
|
34
|
+
* normal error path (which is what AC-3 is for) and resume keeps working.
|
|
35
|
+
*/
|
|
36
|
+
export declare const toSseFrame: (event: RunEvent) => string;
|
|
37
|
+
/**
|
|
38
|
+
* The terminating frame.
|
|
39
|
+
*
|
|
40
|
+
* graphql-sse's distinct-connections mode uses `event: complete` to say the response is finished. A
|
|
41
|
+
* stream that simply stops is indistinguishable from a truncated one, and a client cannot tell whether
|
|
42
|
+
* to reconnect.
|
|
43
|
+
*/
|
|
44
|
+
export declare const SSE_COMPLETE_FRAME = "event: complete\ndata: \n\n";
|
|
45
|
+
/**
|
|
46
|
+
* A stream-level failure, in the protocol's error shape.
|
|
47
|
+
*
|
|
48
|
+
* Distinct from a failed *run*: there is no sequence to preserve and nothing to resume to, so this is
|
|
49
|
+
* genuinely a protocol error rather than an event.
|
|
50
|
+
*/
|
|
51
|
+
export declare const sseErrorFrame: (message: string) => string;
|
|
52
|
+
/** Parse a browser `Last-Event-ID` header into a resume cursor. Missing/invalid ⇒ 0 (from start). */
|
|
53
|
+
export declare const cursorFromLastEventId: (lastEventId: string | null | undefined) => number;
|
|
54
|
+
/**
|
|
55
|
+
* Map a `RunEvent` async stream to graphql-sse text frames, terminated by `complete`.
|
|
56
|
+
*
|
|
57
|
+
* The `complete` frame is emitted in a `finally`, so it is sent whether the stream ended on a terminal
|
|
58
|
+
* event or because the consumer stopped — a client should be told the response is over either way.
|
|
59
|
+
*/
|
|
60
|
+
export declare function sseFrames(events: AsyncIterable<RunEvent>): AsyncIterable<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Open a resumable SSE stream for a run: catch up from the durable log after `after`, then follow
|
|
63
|
+
* live, encoded as SSE frames. Ends on the run's terminal event.
|
|
64
|
+
*/
|
|
65
|
+
export declare const openRunEventSse: (input: {
|
|
66
|
+
readonly tenantId: TenantId;
|
|
67
|
+
readonly runId: RunId;
|
|
68
|
+
readonly conversationId: ConversationId;
|
|
69
|
+
readonly after: number;
|
|
70
|
+
readonly log: RunEventLog;
|
|
71
|
+
readonly live: LiveEventSource;
|
|
72
|
+
readonly channelFor?: (conversationId: ConversationId) => string;
|
|
73
|
+
readonly signal?: {
|
|
74
|
+
readonly aborted: boolean;
|
|
75
|
+
};
|
|
76
|
+
}) => AsyncIterable<string>;
|
|
77
|
+
//# sourceMappingURL=sse.d.ts.map
|