@flowajs/chat-service 0.0.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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/artifact.d.ts +87 -0
  4. package/dist/artifact.d.ts.map +1 -0
  5. package/dist/artifact.js +99 -0
  6. package/dist/artifact.js.map +1 -0
  7. package/dist/audit.d.ts +28 -0
  8. package/dist/audit.d.ts.map +1 -0
  9. package/dist/audit.js +73 -0
  10. package/dist/audit.js.map +1 -0
  11. package/dist/auth/jwt.d.ts +18 -0
  12. package/dist/auth/jwt.d.ts.map +1 -0
  13. package/dist/auth/jwt.js +23 -0
  14. package/dist/auth/jwt.js.map +1 -0
  15. package/dist/auth/oidc.d.ts +30 -0
  16. package/dist/auth/oidc.d.ts.map +1 -0
  17. package/dist/auth/oidc.js +58 -0
  18. package/dist/auth/oidc.js.map +1 -0
  19. package/dist/chat.d.ts +161 -0
  20. package/dist/chat.d.ts.map +1 -0
  21. package/dist/chat.js +636 -0
  22. package/dist/chat.js.map +1 -0
  23. package/dist/cli.d.ts +6 -0
  24. package/dist/cli.d.ts.map +1 -0
  25. package/dist/cli.js +47 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/config.d.ts +18 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +80 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/index.d.ts +19 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +19 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/instrumentation.d.ts +31 -0
  36. package/dist/instrumentation.d.ts.map +1 -0
  37. package/dist/instrumentation.js +151 -0
  38. package/dist/instrumentation.js.map +1 -0
  39. package/dist/llm/anthropic.d.ts +22 -0
  40. package/dist/llm/anthropic.d.ts.map +1 -0
  41. package/dist/llm/anthropic.js +40 -0
  42. package/dist/llm/anthropic.js.map +1 -0
  43. package/dist/llm/bedrock.d.ts +34 -0
  44. package/dist/llm/bedrock.d.ts.map +1 -0
  45. package/dist/llm/bedrock.js +54 -0
  46. package/dist/llm/bedrock.js.map +1 -0
  47. package/dist/llm/factory.d.ts +3 -0
  48. package/dist/llm/factory.d.ts.map +1 -0
  49. package/dist/llm/factory.js +59 -0
  50. package/dist/llm/factory.js.map +1 -0
  51. package/dist/llm/google-gla.d.ts +22 -0
  52. package/dist/llm/google-gla.d.ts.map +1 -0
  53. package/dist/llm/google-gla.js +29 -0
  54. package/dist/llm/google-gla.js.map +1 -0
  55. package/dist/llm/google-vertex.d.ts +21 -0
  56. package/dist/llm/google-vertex.d.ts.map +1 -0
  57. package/dist/llm/google-vertex.js +28 -0
  58. package/dist/llm/google-vertex.js.map +1 -0
  59. package/dist/llm/interface.d.ts +45 -0
  60. package/dist/llm/interface.d.ts.map +1 -0
  61. package/dist/llm/interface.js +2 -0
  62. package/dist/llm/interface.js.map +1 -0
  63. package/dist/llm/openai.d.ts +19 -0
  64. package/dist/llm/openai.d.ts.map +1 -0
  65. package/dist/llm/openai.js +25 -0
  66. package/dist/llm/openai.js.map +1 -0
  67. package/dist/prompts.d.ts +7 -0
  68. package/dist/prompts.d.ts.map +1 -0
  69. package/dist/prompts.js +17 -0
  70. package/dist/prompts.js.map +1 -0
  71. package/dist/server.d.ts +39 -0
  72. package/dist/server.d.ts.map +1 -0
  73. package/dist/server.js +106 -0
  74. package/dist/server.js.map +1 -0
  75. package/dist/session.d.ts +68 -0
  76. package/dist/session.d.ts.map +1 -0
  77. package/dist/session.js +245 -0
  78. package/dist/session.js.map +1 -0
  79. package/dist/storage/factory.d.ts +28 -0
  80. package/dist/storage/factory.d.ts.map +1 -0
  81. package/dist/storage/factory.js +33 -0
  82. package/dist/storage/factory.js.map +1 -0
  83. package/dist/storage/fs.d.ts +14 -0
  84. package/dist/storage/fs.d.ts.map +1 -0
  85. package/dist/storage/fs.js +116 -0
  86. package/dist/storage/fs.js.map +1 -0
  87. package/dist/storage/gcs.d.ts +27 -0
  88. package/dist/storage/gcs.d.ts.map +1 -0
  89. package/dist/storage/gcs.js +81 -0
  90. package/dist/storage/gcs.js.map +1 -0
  91. package/dist/storage/interface.d.ts +33 -0
  92. package/dist/storage/interface.d.ts.map +1 -0
  93. package/dist/storage/interface.js +12 -0
  94. package/dist/storage/interface.js.map +1 -0
  95. package/dist/storage/s3.d.ts +29 -0
  96. package/dist/storage/s3.d.ts.map +1 -0
  97. package/dist/storage/s3.js +109 -0
  98. package/dist/storage/s3.js.map +1 -0
  99. package/dist/storage-keys.d.ts +33 -0
  100. package/dist/storage-keys.d.ts.map +1 -0
  101. package/dist/storage-keys.js +76 -0
  102. package/dist/storage-keys.js.map +1 -0
  103. package/dist/telemetry.d.ts +29 -0
  104. package/dist/telemetry.d.ts.map +1 -0
  105. package/dist/telemetry.js +116 -0
  106. package/dist/telemetry.js.map +1 -0
  107. package/dist/yaml.d.ts +42 -0
  108. package/dist/yaml.d.ts.map +1 -0
  109. package/dist/yaml.js +121 -0
  110. package/dist/yaml.js.map +1 -0
  111. package/package.json +124 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Centre for Population Genomics
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # `@flowajs/chat-service`
2
+
3
+ Stateless service that orchestrates LLM conversations over flowa artifacts.
4
+ Reads aggregates and papers from a configurable storage backend, applies edits
5
+ to a Zod-validated artifact schema, persists draft versions back to storage,
6
+ and streams SSE chat responses.
7
+
8
+ Two consumption modes:
9
+
10
+ - The flowa demo (`examples/demo`) depends on it via `workspace:*` and runs
11
+ the default env-driven entry (`node dist/index.js`).
12
+ - External and production deployments install via npm (`pnpm add
13
+ @flowajs/chat-service`) and wrap the programmatic API (`createApp(...)`)
14
+ with custom storage / LLM / auth wiring. See
15
+ [Production deployment](#production-deployment).
16
+
17
+ ## Architecture
18
+
19
+ ```text
20
+ ┌─ HTTP (Hono) ───────────────────────────────────────────┐
21
+ │ POST /sessions POST /chat/:id GET /health │
22
+ └────────────┬────────────────────────────────────────────┘
23
+
24
+ createApp({ storage, provider, jwtSecret, ... })
25
+
26
+ ┌─────────┴─────────┐
27
+ ▼ ▼
28
+ Storage LlmProvider
29
+ fs | s3 | gcs anthropic | bedrock | google-gla
30
+ google-vertex | openai
31
+ ```
32
+
33
+ The HTTP surface is provider-agnostic. Backends and the LLM provider are
34
+ constructed by the caller (env-driven default in `index.ts`, or programmatic
35
+ in a custom entry).
36
+
37
+ ### Storage
38
+
39
+ `Storage` is a thin interface (5 ops: `read` / `write` / `writeIfAbsent` /
40
+ `exists` / `list`). Three backends are available:
41
+
42
+ - **`fs`** — POSIX local filesystem. Atomic create-only via
43
+ `O_CREAT|O_EXCL`.
44
+ - **`s3`** — `@aws-sdk/client-s3`. Atomic create-only via `IfNoneMatch: '*'`.
45
+ Region, endpoint, and credentials are resolved from the AWS SDK's standard
46
+ env vars (`AWS_REGION`, `AWS_ENDPOINT_URL_S3`, `fromNodeProviderChain`); for
47
+ S3-compatible providers (Cloudflare R2, Backblaze B2, MinIO, DigitalOcean
48
+ Spaces, Wasabi, Hetzner, etc.) set those env vars to point at the provider.
49
+ For knobs the SDK doesn't surface as env vars (e.g. `forcePathStyle`,
50
+ custom retry policy), use the `{ client }` programmatic form.
51
+ - **`gcs`** — `@google-cloud/storage`. Atomic create-only via the
52
+ `ifGenerationMatch: 0` precondition. Credentials come from Google Cloud's
53
+ Application Default Credentials chain (`GOOGLE_APPLICATION_CREDENTIALS`,
54
+ gcloud user creds, GCE metadata server, etc.). For deployments needing
55
+ Workload Identity Federation or other custom cred-mint flows, use the
56
+ `{ client }` programmatic form with a pre-built `Storage` client.
57
+
58
+ An Azure Blob backend is the next adapter when a consumer surfaces it; same
59
+ factory shape (`{ bucket, prefix? } | { client, bucket, prefix? }`).
60
+
61
+ ### LLM providers
62
+
63
+ `LlmProvider` is a thin interface around a Vercel AI SDK `LanguageModel` plus
64
+ two optional knobs: `providerOptions` (per-provider thinking/reasoning
65
+ config) and `prepareStep(messages)` (per-step messages transformation, used
66
+ by Bedrock for prompt-cache point injection).
67
+
68
+ Five providers are supported: `anthropic`, `bedrock`, `google-gla`,
69
+ `google-vertex`, `openai`. Each ai-sdk package is an optional peer — install
70
+ only what you use. Selection at runtime via `LLM_MODEL=<provider>:<model>`.
71
+
72
+ ### Authentication
73
+
74
+ chat-service issues and verifies its own session JWT (`POST /sessions` →
75
+ token; `POST /chat/:id` requires the token). Upstream-IDP authentication
76
+ (validating that the caller of `POST /sessions` is who they claim) is a
77
+ separate concern, exposed as a generic OIDC middleware:
78
+
79
+ ```ts
80
+ import { createOidcMiddleware } from "@flowajs/chat-service/auth/oidc";
81
+
82
+ app.use("/sessions", createOidcMiddleware({
83
+ jwksUrl: process.env.OIDC_JWKS_URL!,
84
+ issuer: process.env.OIDC_ISSUER!,
85
+ audience: process.env.OIDC_AUDIENCE!,
86
+ // dev mode: decode the JWT body without verifying signature.
87
+ // Useful for local dev with a stub IDP. NEVER enable in production.
88
+ devMode: process.env.NODE_ENV === "development",
89
+ }));
90
+ ```
91
+
92
+ The middleware works against any OIDC IDP (Auth0, Keycloak, Okta, GitHub,
93
+ etc.). The default env-driven `index.ts` applies it on `/sessions` when
94
+ `OIDC_JWKS_URL` is set; otherwise the route is unauthenticated.
95
+
96
+ ## Environment configuration (default `index.ts`)
97
+
98
+ | Var | Required? | Purpose |
99
+ |-----|-----------|---------|
100
+ | `LLM_MODEL` | yes | `<provider>:<model>` — e.g. `bedrock:au.anthropic.claude-sonnet-4-6`, `anthropic:claude-sonnet-4-6`, `google-gla:gemini-2.5-pro`, `google-vertex:gemini-2.5-pro`, `openai:gpt-5`. |
101
+ | `STORAGE_BACKEND` | yes | One of `fs`, `s3`, `gcs`. |
102
+ | `STORAGE_FS_ROOT` | when `fs` | Absolute path to the storage root directory. |
103
+ | `STORAGE_S3_BUCKET` | when `s3` | Bucket name. Region, endpoint, and credentials come from the AWS SDK's standard env vars (`AWS_REGION`, `AWS_ENDPOINT_URL_S3`, `AWS_ACCESS_KEY_ID`, etc.); set those to point at AWS S3 or any S3-compat provider. |
104
+ | `STORAGE_GCS_BUCKET` | when `gcs` | Bucket name. Credentials come from Google Cloud's Application Default Credentials chain (`GOOGLE_APPLICATION_CREDENTIALS`, gcloud user creds, GCE metadata server, etc.). |
105
+ | `STORAGE_PREFIX` | no | Prefix prepended to every storage key (regardless of backend). |
106
+ | `CHAT_JWT_SECRET` | yes | Session JWT signing key. |
107
+ | `CHAT_PROMPT_DIR` | no, default `./prompts` | Directory containing `aggregation_edit_prompt.txt`. |
108
+ | `CHAT_JWT_TTL_SECONDS` | no, default `14400` | Session token lifetime (4h). |
109
+ | `PORT` | no, default `8000` | HTTP listen port. |
110
+ | `OTEL_ENABLED` | no, default `false` | When `true`, `instrumentation.ts` boots the OpenTelemetry SDK with SigV4 OTLP transport. The demo leaves this unset; production sets it to enable CloudWatch / X-Ray. |
111
+ | `BEDROCK_INFERENCE_PROFILE` | no | Application inference profile ARN for Bedrock cost attribution; only used when `LLM_MODEL` starts with `bedrock:`. |
112
+ | `OIDC_JWKS_URL`, `OIDC_ISSUER`, `OIDC_AUDIENCE` | no | When all three are set, `index.ts` applies the OIDC middleware on `POST /sessions`. |
113
+
114
+ Provider creds are read by each ai-sdk package via standard env vars
115
+ (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`,
116
+ `GOOGLE_APPLICATION_CREDENTIALS` + `GOOGLE_VERTEX_PROJECT` +
117
+ `GOOGLE_VERTEX_LOCATION`, `AWS_REGION` + `AWS_ACCESS_KEY_ID` etc.). AWS S3
118
+ storage uses the AWS SDK's default credential chain
119
+ (`fromNodeProviderChain`).
120
+
121
+ ## HTTP API
122
+
123
+ - `POST /sessions { variant_id, user_id, category, initial_artifact, initial_version }`
124
+ → `{ session_id, token, expires_at }`
125
+ - `POST /chat/:sessionId { messages, triage_state? }` → SSE stream
126
+ - `GET /health` → `{ status: "ok" }`
127
+
128
+ ## Production deployment
129
+
130
+ The default `index.ts` is sufficient when standard credential chains
131
+ (AWS env vars, IAM role, IRSA, etc.) cover your cred-mint flow. For
132
+ deployments that need custom cred minting (e.g. minting AWS STS credentials
133
+ from an OIDC IDP token in-process), write a small entry that uses the
134
+ programmatic API and injects pre-built SDK clients:
135
+
136
+ ```ts
137
+ import { serve } from "@hono/node-server";
138
+ import { Hono } from "hono";
139
+ import { S3Client } from "@aws-sdk/client-s3";
140
+ import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock";
141
+ import { createApp } from "@flowajs/chat-service/server";
142
+ import { createS3Storage } from "@flowajs/chat-service/storage/s3";
143
+ import { createBedrockProvider } from "@flowajs/chat-service/llm/bedrock";
144
+ import { createOidcMiddleware } from "@flowajs/chat-service/auth/oidc";
145
+ import { yourCustomCredentialProvider } from "./creds.js";
146
+
147
+ // Build a credential provider that refreshes on expiry. The AWS SDK
148
+ // invokes it on each request that needs creds.
149
+ const credentials = yourCustomCredentialProvider();
150
+
151
+ // Storage: pre-built S3Client bound to the custom credential provider.
152
+ const s3 = new S3Client({ region: "us-east-1", credentials });
153
+ const storage = createS3Storage({
154
+ client: s3,
155
+ bucket: "my-bucket",
156
+ prefix: "flowa/",
157
+ });
158
+
159
+ // LLM: pre-built bedrock client bound to the same credential provider.
160
+ const bedrockClient = createAmazonBedrock({
161
+ region: "us-east-1",
162
+ credentialProvider: async () => {
163
+ const c = await credentials();
164
+ return {
165
+ accessKeyId: c.accessKeyId,
166
+ secretAccessKey: c.secretAccessKey,
167
+ sessionToken: c.sessionToken,
168
+ };
169
+ },
170
+ });
171
+ const provider = createBedrockProvider({
172
+ modelId: "us.anthropic.claude-sonnet-4-6",
173
+ client: bedrockClient,
174
+ });
175
+
176
+ const chatApp = createApp({
177
+ storage,
178
+ provider,
179
+ jwtSecret: process.env.CHAT_JWT_SECRET!,
180
+ promptDir: "./prompts",
181
+ });
182
+
183
+ // Compose with custom auth middleware. The OIDC validator shipped with
184
+ // chat-service handles standard JWKS verification; substitute your own
185
+ // middleware if your IDP needs something else.
186
+ const app = new Hono();
187
+ app.use("/sessions", createOidcMiddleware({
188
+ jwksUrl: process.env.OIDC_JWKS_URL!,
189
+ issuer: process.env.OIDC_ISSUER!,
190
+ audience: process.env.OIDC_AUDIENCE!,
191
+ }));
192
+ app.route("/", chatApp);
193
+
194
+ serve({ fetch: app.fetch, port: 8000 });
195
+ ```
196
+
197
+ ## Build and run
198
+
199
+ ```bash
200
+ pnpm install
201
+ pnpm --filter @flowajs/chat-service typecheck
202
+ pnpm --filter @flowajs/chat-service test
203
+ pnpm --filter @flowajs/chat-service build
204
+ node packages/chat-service/dist/index.js
205
+ ```
206
+
207
+ A reference `Dockerfile` is included as a starting point. Production
208
+ deployments that need a custom entry install `@flowajs/chat-service` from
209
+ npm and write their own thin entry that calls `createApp(...)` — see
210
+ [Production deployment](#production-deployment).
@@ -0,0 +1,87 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Citation-grounded fields shared by every chat-service artifact. Deployments
4
+ * extend the schema by calling `ArtifactSchema.extend({...})` with their
5
+ * additional fields:
6
+ *
7
+ * ```ts
8
+ * import { ArtifactSchema } from "@flowajs/chat-service";
9
+ *
10
+ * export const MyArtifactSchema = ArtifactSchema.extend({
11
+ * classification: z.string(),
12
+ * classification_rationale: z.string(),
13
+ * });
14
+ * ```
15
+ *
16
+ * `.extend(...)` preserves the shape's TypeScript type, so the resulting
17
+ * schema stays assignable to `z.ZodType<Artifact>` at the
18
+ * `createApp({ schema })` call site. Spreading `...artifactFields` into a
19
+ * fresh `z.object({...})` works at runtime but widens the inferred shape
20
+ * to `Record<string, any>`, which fails the static type. `artifactFields`
21
+ * is exported anyway for runtime inspection / reflection.
22
+ *
23
+ * Deployments must keep these fields with these names; chat-service reads
24
+ * them directly. Refining their types (e.g. constraining `category` to a
25
+ * Zod enum) is fine; renaming is not.
26
+ */
27
+ export declare const artifactFields: {
28
+ readonly category: z.ZodString;
29
+ readonly description: z.ZodString;
30
+ readonly notes: z.ZodString;
31
+ readonly papers: z.ZodArray<z.ZodObject<{
32
+ paper_id: z.ZodString;
33
+ rank_rationale: z.ZodString;
34
+ }, z.core.$strip>>;
35
+ readonly claims: z.ZodArray<z.ZodObject<{
36
+ paper_id: z.ZodString;
37
+ text: z.ZodString;
38
+ citations: z.ZodArray<z.ZodObject<{
39
+ quote: z.ZodString;
40
+ bboxes: z.ZodOptional<z.ZodArray<z.ZodObject<{
41
+ page: z.ZodNumber;
42
+ top: z.ZodNumber;
43
+ left: z.ZodNumber;
44
+ bottom: z.ZodNumber;
45
+ right: z.ZodNumber;
46
+ }, z.core.$strip>>>;
47
+ }, z.core.$strip>>;
48
+ }, z.core.$strip>>;
49
+ };
50
+ /**
51
+ * The minimal Zod schema chat-service can operate on. Any plugin's schema is
52
+ * a superset: it spreads `artifactFields` and adds deployment fields.
53
+ */
54
+ export declare const ArtifactSchema: z.ZodObject<{
55
+ category: z.ZodString;
56
+ description: z.ZodString;
57
+ notes: z.ZodString;
58
+ papers: z.ZodArray<z.ZodObject<{
59
+ paper_id: z.ZodString;
60
+ rank_rationale: z.ZodString;
61
+ }, z.core.$strip>>;
62
+ claims: z.ZodArray<z.ZodObject<{
63
+ paper_id: z.ZodString;
64
+ text: z.ZodString;
65
+ citations: z.ZodArray<z.ZodObject<{
66
+ quote: z.ZodString;
67
+ bboxes: z.ZodOptional<z.ZodArray<z.ZodObject<{
68
+ page: z.ZodNumber;
69
+ top: z.ZodNumber;
70
+ left: z.ZodNumber;
71
+ bottom: z.ZodNumber;
72
+ right: z.ZodNumber;
73
+ }, z.core.$strip>>>;
74
+ }, z.core.$strip>>;
75
+ }, z.core.$strip>>;
76
+ }, z.core.$strip>;
77
+ /**
78
+ * The citation-grounded artifact type. Deployments may name their own
79
+ * extended type; this is the base chat-service operates on.
80
+ */
81
+ export type Artifact = z.infer<typeof ArtifactSchema>;
82
+ /**
83
+ * Render a Zod schema as a JSON Schema string for inclusion in the system
84
+ * prompt. The descriptions on each field serve as documentation.
85
+ */
86
+ export declare function schemaForPrompt(schema: z.ZodType): string;
87
+ //# sourceMappingURL=artifact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../src/artifact.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoDxB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;CAwBjB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;iBAA2B,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,99 @@
1
+ // Citation-grounded artifact core. chat-service hardcodes a small fixed
2
+ // set of fields (`category`, `description`, `notes`, `papers`, `claims`)
3
+ // because those are what the chat-tool design — `view`, `search`,
4
+ // `str_replace`, `insert`, `write` over a citation-grounded synthesis —
5
+ // actually depends on. Deployments extend by calling
6
+ // `ArtifactSchema.extend({...})` with their additional fields; they do
7
+ // not rename the core fields.
8
+ //
9
+ // Field names mirror flowa's `prompts/generic/aggregation_schema.py`.
10
+ import { z } from "zod";
11
+ const CitationBbox = z.object({
12
+ page: z.number().describe("Page number in the source document."),
13
+ top: z.number().describe("Top edge of the highlight rectangle."),
14
+ left: z.number().describe("Left edge of the highlight rectangle."),
15
+ bottom: z.number().describe("Bottom edge of the highlight rectangle."),
16
+ right: z.number().describe("Right edge of the highlight rectangle."),
17
+ });
18
+ const ArtifactCitation = z.object({
19
+ quote: z
20
+ .string()
21
+ .describe("Verbatim passage from the source document. Must be copied exactly — enough context to validate the claim."),
22
+ bboxes: z
23
+ .array(CitationBbox)
24
+ .optional()
25
+ .describe("Pre-computed highlight rectangles for the quote. Present on pipeline-generated citations; absent on citations added during editing."),
26
+ });
27
+ const ArtifactClaim = z.object({
28
+ paper_id: z
29
+ .string()
30
+ .describe("Identifier of the source document. Must appear in the papers[] list."),
31
+ text: z
32
+ .string()
33
+ .describe("The factual statement supported by the source document. One atomic fact per claim."),
34
+ citations: z
35
+ .array(ArtifactCitation)
36
+ .min(1)
37
+ .describe("One or more supporting verbatim quotes from the document identified by paper_id."),
38
+ });
39
+ const RankedPaper = z.object({
40
+ paper_id: z.string().describe("Identifier of the source document."),
41
+ rank_rationale: z
42
+ .string()
43
+ .describe("One sentence explaining why this document sits at this rank in the synthesis."),
44
+ });
45
+ /**
46
+ * Citation-grounded fields shared by every chat-service artifact. Deployments
47
+ * extend the schema by calling `ArtifactSchema.extend({...})` with their
48
+ * additional fields:
49
+ *
50
+ * ```ts
51
+ * import { ArtifactSchema } from "@flowajs/chat-service";
52
+ *
53
+ * export const MyArtifactSchema = ArtifactSchema.extend({
54
+ * classification: z.string(),
55
+ * classification_rationale: z.string(),
56
+ * });
57
+ * ```
58
+ *
59
+ * `.extend(...)` preserves the shape's TypeScript type, so the resulting
60
+ * schema stays assignable to `z.ZodType<Artifact>` at the
61
+ * `createApp({ schema })` call site. Spreading `...artifactFields` into a
62
+ * fresh `z.object({...})` works at runtime but widens the inferred shape
63
+ * to `Record<string, any>`, which fails the static type. `artifactFields`
64
+ * is exported anyway for runtime inspection / reflection.
65
+ *
66
+ * Deployments must keep these fields with these names; chat-service reads
67
+ * them directly. Refining their types (e.g. constraining `category` to a
68
+ * Zod enum) is fine; renaming is not.
69
+ */
70
+ export const artifactFields = {
71
+ category: z
72
+ .string()
73
+ .describe("Selector identifying which result this artifact represents within its aggregate. Mirrors aggregation.results[].category in flowa's prompts/generic/aggregation_schema.py."),
74
+ description: z
75
+ .string()
76
+ .describe("Short human-readable summary of the synthesis."),
77
+ notes: z
78
+ .string()
79
+ .describe('Long-form synthesis in Markdown. Uses inline citation links: [text](#cite:paper_id "verbatim quote").'),
80
+ papers: z
81
+ .array(RankedPaper)
82
+ .describe("Source documents contributing to the synthesis, ordered by contribution. List position is the rank. paper_id values must be unique."),
83
+ claims: z
84
+ .array(ArtifactClaim)
85
+ .describe("Factual claims supporting the synthesis. Grouped by paper_id in the same order as papers[]."),
86
+ };
87
+ /**
88
+ * The minimal Zod schema chat-service can operate on. Any plugin's schema is
89
+ * a superset: it spreads `artifactFields` and adds deployment fields.
90
+ */
91
+ export const ArtifactSchema = z.object(artifactFields);
92
+ /**
93
+ * Render a Zod schema as a JSON Schema string for inclusion in the system
94
+ * prompt. The descriptions on each field serve as documentation.
95
+ */
96
+ export function schemaForPrompt(schema) {
97
+ return JSON.stringify(z.toJSONSchema(schema), null, 2);
98
+ }
99
+ //# sourceMappingURL=artifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact.js","sourceRoot":"","sources":["../src/artifact.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,yEAAyE;AACzE,kEAAkE;AAClE,wEAAwE;AACxE,qDAAqD;AACrD,uEAAuE;AACvE,8BAA8B;AAC9B,EAAE;AACF,sEAAsE;AAEtE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;IAChE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;IAChE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAClE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;IACtE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;CACrE,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,CACP,2GAA2G,CAC5G;IACH,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,YAAY,CAAC;SACnB,QAAQ,EAAE;SACV,QAAQ,CACP,qIAAqI,CACtI;CACJ,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,CACP,sEAAsE,CACvE;IACH,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CACP,oFAAoF,CACrF;IACH,SAAS,EAAE,CAAC;SACT,KAAK,CAAC,gBAAgB,CAAC;SACvB,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,kFAAkF,CACnF;CACJ,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IACnE,cAAc,EAAE,CAAC;SACd,MAAM,EAAE;SACR,QAAQ,CACP,+EAA+E,CAChF;CACJ,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,CACP,2KAA2K,CAC5K;IACH,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,CAAC,gDAAgD,CAAC;IAC7D,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,CACP,uGAAuG,CACxG;IACH,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,WAAW,CAAC;SAClB,QAAQ,CACP,qIAAqI,CACtI;IACH,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,aAAa,CAAC;SACpB,QAAQ,CACP,6FAA6F,CAC9F;CACK,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAQvD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,28 @@
1
+ /** Audit logging — per-session JSON files written to storage. */
2
+ import type { UIMessage } from "ai";
3
+ import type { Storage } from "./storage/interface.js";
4
+ export declare function logRequest(params: {
5
+ sessionId: string;
6
+ userId: string;
7
+ variantId: string;
8
+ messages: UIMessage[];
9
+ }): Promise<void>;
10
+ export declare function logStep(params: {
11
+ sessionId: string;
12
+ finishReason: string;
13
+ text: string;
14
+ toolCalls: unknown[];
15
+ toolResults: unknown[];
16
+ }): Promise<void>;
17
+ export declare function markTurnTruncated(sessionId: string): void;
18
+ export declare function logResponse(storage: Storage, params: {
19
+ sessionId: string;
20
+ userId: string;
21
+ response: string;
22
+ toolCalls: unknown;
23
+ usage: unknown;
24
+ }): Promise<void>;
25
+ export declare function logError(storage: Storage, sessionId: string, error: string): Promise<void>;
26
+ /** For tests: clear the per-session audit log map. */
27
+ export declare function clearAuditLogs(): void;
28
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAgDtD,wBAAsB,UAAU,CAAC,MAAM,EAAE;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,EAAE,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,EAAE,CAAC;IACrB,WAAW,EAAE,OAAO,EAAE,CAAC;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE;IACN,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB,GACA,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,sDAAsD;AACtD,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
package/dist/audit.js ADDED
@@ -0,0 +1,73 @@
1
+ /** Audit logging — per-session JSON files written to storage. */
2
+ import { auditLogKey } from "./storage-keys.js";
3
+ /** In-memory accumulator per session, flushed to storage after each turn completes. */
4
+ const logs = new Map();
5
+ function getOrCreateLog(sessionId, userId, variantId) {
6
+ let log = logs.get(sessionId);
7
+ if (!log) {
8
+ log = { sessionId, userId, variantId, turns: [] };
9
+ logs.set(sessionId, log);
10
+ }
11
+ return log;
12
+ }
13
+ export async function logRequest(params) {
14
+ const log = getOrCreateLog(params.sessionId, params.userId, params.variantId);
15
+ log.turns.push({
16
+ timestamp: new Date().toISOString(),
17
+ request: { messages: params.messages },
18
+ steps: [],
19
+ });
20
+ }
21
+ export async function logStep(params) {
22
+ const log = logs.get(params.sessionId);
23
+ if (!log)
24
+ return;
25
+ const lastTurn = log.turns.at(-1);
26
+ if (lastTurn) {
27
+ lastTurn.steps ??= [];
28
+ lastTurn.steps.push({
29
+ finishReason: params.finishReason,
30
+ text: params.text,
31
+ toolCalls: params.toolCalls,
32
+ toolResults: params.toolResults,
33
+ });
34
+ }
35
+ }
36
+ export function markTurnTruncated(sessionId) {
37
+ const log = logs.get(sessionId);
38
+ if (!log)
39
+ return;
40
+ const lastTurn = log.turns.at(-1);
41
+ if (lastTurn) {
42
+ lastTurn.truncated = true;
43
+ }
44
+ }
45
+ export async function logResponse(storage, params) {
46
+ const log = logs.get(params.sessionId);
47
+ if (!log)
48
+ return;
49
+ const lastTurn = log.turns.at(-1);
50
+ if (lastTurn) {
51
+ lastTurn.response = {
52
+ text: params.response,
53
+ toolCalls: params.toolCalls,
54
+ usage: params.usage,
55
+ };
56
+ }
57
+ await storage.writeJson(auditLogKey(log.variantId, log.sessionId), log);
58
+ }
59
+ export async function logError(storage, sessionId, error) {
60
+ const log = logs.get(sessionId);
61
+ if (!log)
62
+ return;
63
+ const lastTurn = log.turns.at(-1);
64
+ if (lastTurn) {
65
+ lastTurn.error = error;
66
+ }
67
+ await storage.writeJson(auditLogKey(log.variantId, log.sessionId), log);
68
+ }
69
+ /** For tests: clear the per-session audit log map. */
70
+ export function clearAuditLogs() {
71
+ logs.clear();
72
+ }
73
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface SessionClaims {
2
+ session_id: string;
3
+ variant_id: string;
4
+ user_id: string;
5
+ category: string;
6
+ }
7
+ export interface SessionTokenConfig {
8
+ secret: string;
9
+ ttlSeconds: number;
10
+ }
11
+ export declare function signSessionToken(claims: SessionClaims, config: SessionTokenConfig): Promise<{
12
+ token: string;
13
+ expiresAt: Date;
14
+ }>;
15
+ export declare function verifySessionToken(token: string, config: SessionTokenConfig): Promise<SessionClaims & {
16
+ expiresAt: Date;
17
+ }>;
18
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAS7C;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAU9C"}
@@ -0,0 +1,23 @@
1
+ import { SignJWT, jwtVerify } from "jose";
2
+ export async function signSessionToken(claims, config) {
3
+ const expiresAt = new Date(Date.now() + config.ttlSeconds * 1000);
4
+ const secret = new TextEncoder().encode(config.secret);
5
+ const token = await new SignJWT(claims)
6
+ .setProtectedHeader({ alg: "HS256" })
7
+ .setExpirationTime(expiresAt)
8
+ .setIssuedAt()
9
+ .sign(secret);
10
+ return { token, expiresAt };
11
+ }
12
+ export async function verifySessionToken(token, config) {
13
+ const secret = new TextEncoder().encode(config.secret);
14
+ const { payload } = await jwtVerify(token, secret);
15
+ return {
16
+ session_id: payload.session_id,
17
+ variant_id: payload.variant_id,
18
+ user_id: payload.user_id,
19
+ category: payload.category,
20
+ expiresAt: new Date(payload.exp * 1000),
21
+ };
22
+ }
23
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAc1C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAqB,EACrB,MAA0B;IAE1B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAAC,MAA4C,CAAC;SAC1E,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;SACpC,iBAAiB,CAAC,SAAS,CAAC;SAC5B,WAAW,EAAE;SACb,IAAI,CAAC,MAAM,CAAC,CAAC;IAChB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,MAA0B;IAE1B,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnD,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,UAAoB;QACxC,UAAU,EAAE,OAAO,CAAC,UAAoB;QACxC,OAAO,EAAE,OAAO,CAAC,OAAiB;QAClC,QAAQ,EAAE,OAAO,CAAC,QAAkB;QACpC,SAAS,EAAE,IAAI,IAAI,CAAE,OAAO,CAAC,GAAc,GAAG,IAAI,CAAC;KACpD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { type JWTPayload } from "jose";
2
+ import type { MiddlewareHandler } from "hono";
3
+ export interface OidcValidatorConfig {
4
+ jwksUrl: string;
5
+ issuer: string;
6
+ audience: string;
7
+ /**
8
+ * When true, decode the JWT body without verifying the signature. Useful
9
+ * for local development against a stub IDP. NEVER enable in production —
10
+ * the token is trusted as-is.
11
+ */
12
+ devMode?: boolean;
13
+ }
14
+ /**
15
+ * Validate a Bearer token from an Authorization header against an OIDC
16
+ * IDP. Returns the decoded claims on success; throws on missing header,
17
+ * malformed token, or signature/audience/issuer mismatch.
18
+ *
19
+ * Generic over IDP — works against any OIDC-compliant provider (Auth0,
20
+ * Keycloak, Okta, GitHub, etc.).
21
+ */
22
+ export declare function validateOidcToken(authHeader: string | undefined, config: OidcValidatorConfig): Promise<JWTPayload>;
23
+ /**
24
+ * Hono middleware factory that validates the Authorization header against
25
+ * the configured OIDC IDP. The decoded claims are stored on the context
26
+ * variable `oidcClaims` for downstream handlers to read via
27
+ * `c.get("oidcClaims")`.
28
+ */
29
+ export declare function createOidcMiddleware(config: OidcValidatorConfig): MiddlewareHandler;
30
+ //# sourceMappingURL=oidc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.d.ts","sourceRoot":"","sources":["../../src/auth/oidc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAaD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,UAAU,CAAC,CA0BrB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,mBAAmB,GAC1B,iBAAiB,CAanB"}