@noodleseed/one 0.136.0 → 0.138.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/commands/assistant-ops.d.ts.map +1 -1
  2. package/dist/commands/assistant-ops.js +30 -1
  3. package/dist/commands/assistant-ops.js.map +1 -1
  4. package/dist/commands/deploy-first-flow.d.ts +2 -0
  5. package/dist/commands/deploy-first-flow.d.ts.map +1 -1
  6. package/dist/commands/deploy-first-flow.js +46 -14
  7. package/dist/commands/deploy-first-flow.js.map +1 -1
  8. package/dist/commands/deploy-ops.d.ts.map +1 -1
  9. package/dist/commands/deploy-ops.js +27 -1
  10. package/dist/commands/deploy-ops.js.map +1 -1
  11. package/dist/commands/deploy-preflight.d.ts +1 -0
  12. package/dist/commands/deploy-preflight.d.ts.map +1 -1
  13. package/dist/commands/deploy-preflight.js +1 -0
  14. package/dist/commands/deploy-preflight.js.map +1 -1
  15. package/dist/commands/deploy-status-ops.d.ts +1 -0
  16. package/dist/commands/deploy-status-ops.d.ts.map +1 -1
  17. package/dist/commands/deploy-status-ops.js +59 -4
  18. package/dist/commands/deploy-status-ops.js.map +1 -1
  19. package/dist/commands/deployments-ops.d.ts.map +1 -1
  20. package/dist/commands/deployments-ops.js +2 -0
  21. package/dist/commands/deployments-ops.js.map +1 -1
  22. package/dist/commands/diagnostics-ops.d.ts +1 -0
  23. package/dist/commands/diagnostics-ops.d.ts.map +1 -1
  24. package/dist/commands/diagnostics-ops.js +3 -0
  25. package/dist/commands/diagnostics-ops.js.map +1 -1
  26. package/dist/deploy.d.ts +3 -0
  27. package/dist/deploy.d.ts.map +1 -1
  28. package/dist/deploy.js +4 -0
  29. package/dist/deploy.js.map +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/index.js.map +1 -1
  34. package/node_modules/@noodle-borg/admission-limits/dist/envelope.js +1 -1
  35. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +4 -4
  36. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +1 -0
  37. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +22 -13
  38. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  39. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.d.ts +6 -2
  40. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.js +5 -2
  41. package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +37 -0
  42. package/node_modules/@noodle-borg/assistant-gateway/dist/model-runtime.js +4 -0
  43. package/node_modules/@noodle-borg/{service/dist/assistant-model-stream.js → assistant-gateway/dist/model-stream.js} +34 -2
  44. package/node_modules/@noodle-borg/assistant-gateway/dist/public-session.d.ts +3 -0
  45. package/node_modules/@noodle-borg/assistant-gateway/dist/public-session.js +2 -1
  46. package/node_modules/@noodle-borg/assistant-gateway/dist/public-turn.d.ts +4 -1
  47. package/node_modules/@noodle-borg/assistant-gateway/dist/public-turn.js +2 -1
  48. package/node_modules/@noodle-borg/assistant-gateway/dist/surface-budget.d.ts +6 -1
  49. package/node_modules/@noodle-borg/assistant-gateway/dist/surface-budget.js +11 -5
  50. package/node_modules/@noodle-borg/assistant-gateway/package.json +5 -1
  51. package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +10 -2
  52. package/node_modules/@noodle-borg/authoring/dist/assistant.js +4 -0
  53. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -1
  54. package/node_modules/@noodle-borg/authoring/dist/index.js +1 -1
  55. package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
  56. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-assistant.js +17 -0
  57. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-hosted-deployment.js +14 -0
  58. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +1 -7
  59. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +20 -8
  60. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +12 -8
  61. package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +2 -0
  62. package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
  63. package/node_modules/@noodle-borg/control-plane/dist/rollback-deployment.d.ts +61 -0
  64. package/node_modules/@noodle-borg/{service/dist/operations → control-plane/dist}/rollback-deployment.js +3 -2
  65. package/node_modules/@noodle-borg/control-plane/dist/rollback-result.d.ts +19 -0
  66. package/node_modules/@noodle-borg/control-plane/package.json +3 -1
  67. package/node_modules/@noodle-borg/developer-mcp/dist/contracts.js +4 -0
  68. package/node_modules/@noodle-borg/developer-mcp/dist/widgets/app-overview.js +1 -1
  69. package/node_modules/@noodle-borg/developer-mcp/dist/widgets/deployment-detail.js +1 -1
  70. package/node_modules/@noodle-borg/module/dist/contract.d.ts +1 -0
  71. package/node_modules/@noodle-borg/module/dist/deployment-automation.d.ts +1 -1
  72. package/node_modules/@noodle-borg/module/dist/endpoint-url.d.ts +11 -0
  73. package/node_modules/@noodle-borg/module/dist/endpoint-url.js +20 -0
  74. package/node_modules/@noodle-borg/module/dist/index.d.ts +1 -0
  75. package/node_modules/@noodle-borg/module/dist/index.js +1 -0
  76. package/node_modules/@noodle-borg/observability/dist/assistant-usage.js +217 -0
  77. package/node_modules/@noodle-borg/observability/dist/index.js +2 -0
  78. package/node_modules/@noodle-borg/{service → observability}/dist/request-event-metrics.js +5 -2
  79. package/node_modules/@noodle-borg/observability/dist/request-events-postgres.js +6 -0
  80. package/node_modules/@noodle-borg/observability/dist/request-events.js +2 -0
  81. package/node_modules/@noodle-borg/service/dist/assistant-bindings.js +3 -1
  82. package/node_modules/@noodle-borg/service/dist/developer-mcp/control-plane-adapter.js +3 -3
  83. package/node_modules/@noodle-borg/service/dist/developer-mcp/inspection.js +2 -1
  84. package/node_modules/@noodle-borg/service/dist/registry-access.js +73 -24
  85. package/node_modules/@noodle-borg/service/dist/registry-deploy-transaction.js +5 -2
  86. package/node_modules/@noodle-borg/service/dist/registry-helpers.d.ts +5 -9
  87. package/node_modules/@noodle-borg/service/dist/registry-helpers.js +4 -18
  88. package/node_modules/@noodle-borg/service/dist/registry-rollback.js +3 -1
  89. package/node_modules/@noodle-borg/service/dist/registry-status.js +4 -1
  90. package/node_modules/@noodle-borg/service/dist/registry-targets.js +4 -4
  91. package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +11 -16
  92. package/node_modules/@noodle-borg/service/dist/registry.d.ts +8 -5
  93. package/node_modules/@noodle-borg/service/dist/registry.js +8 -5
  94. package/node_modules/@noodle-borg/service/dist/routes/access.js +19 -1
  95. package/node_modules/@noodle-borg/service/dist/routes/analytics-dispatch.js +17 -1
  96. package/node_modules/@noodle-borg/service/dist/routes/analytics.js +1 -1
  97. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +73 -59
  98. package/node_modules/@noodle-borg/service/dist/routes/assistant-doctor.js +1 -1
  99. package/node_modules/@noodle-borg/service/dist/routes/assistant-model-binding.js +28 -0
  100. package/node_modules/@noodle-borg/service/dist/routes/assistant-public-session.js +18 -0
  101. package/node_modules/@noodle-borg/service/dist/routes/assistant-public-turn.js +6 -0
  102. package/node_modules/@noodle-borg/service/dist/routes/assistant-usage.js +42 -0
  103. package/node_modules/@noodle-borg/service/dist/routes/assistant.js +20 -3
  104. package/node_modules/@noodle-borg/service/dist/routes/auth-doctor-live.js +1 -2
  105. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +49 -4
  106. package/node_modules/@noodle-borg/service/dist/routes/deploy-preflight.js +11 -1
  107. package/node_modules/@noodle-borg/service/dist/routes/endpoint-enrichment.js +2 -1
  108. package/node_modules/@noodle-borg/service/dist/routes/paths.js +4 -0
  109. package/node_modules/@noodle-borg/service/dist/routes/rollback.js +1 -1
  110. package/node_modules/@noodle-borg/service/dist/service.js +6 -0
  111. package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +2 -5
  112. package/node_modules/@noodle-borg/service/dist/store/in-memory.js +9 -21
  113. package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +2 -5
  114. package/node_modules/@noodle-borg/service/dist/store/json-file.js +8 -20
  115. package/node_modules/@noodle-borg/service/dist/store/records.js +39 -0
  116. package/node_modules/@noodle-borg/service/dist/store.d.ts +21 -10
  117. package/node_modules/@noodle-borg/service/package.json +5 -1
  118. package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.d.ts +120 -0
  119. package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.js +123 -1
  120. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +4 -0
  121. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +15 -1
  122. package/node_modules/@noodleseed/assistant/README.md +43 -8
  123. package/node_modules/@noodleseed/assistant/package.json +16 -2
  124. package/package.json +2 -2
@@ -231,6 +231,7 @@ export declare const manifestV1Schema: z.ZodObject<{
231
231
  copy: z.ZodOptional<z.ZodString>;
232
232
  newMessages: z.ZodOptional<z.ZodString>;
233
233
  reconnect: z.ZodOptional<z.ZodString>;
234
+ newConversation: z.ZodOptional<z.ZodString>;
234
235
  retry: z.ZodOptional<z.ZodString>;
235
236
  unavailable: z.ZodOptional<z.ZodString>;
236
237
  sessionExpired: z.ZodOptional<z.ZodString>;
@@ -325,12 +326,14 @@ export declare const manifestV1Schema: z.ZodObject<{
325
326
  ltr: "ltr";
326
327
  rtl: "rtl";
327
328
  }>>;
328
- model: z.ZodObject<{
329
+ model: z.ZodUnion<readonly [z.ZodObject<{
329
330
  kind: z.ZodLiteral<"openai-compatible">;
330
331
  baseUrl: z.ZodString;
331
332
  model: z.ZodString;
332
333
  apiKey: z.ZodString;
333
- }, z.core.$strict>;
334
+ }, z.core.$strict>, z.ZodObject<{
335
+ kind: z.ZodLiteral<"noodle-managed">;
336
+ }, z.core.$strict>]>;
334
337
  allowedOrigins: z.ZodArray<z.ZodString>;
335
338
  surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
336
339
  mode: z.ZodEnum<{
@@ -682,6 +685,7 @@ export declare const manifestV2Schema: z.ZodObject<{
682
685
  copy: z.ZodOptional<z.ZodString>;
683
686
  newMessages: z.ZodOptional<z.ZodString>;
684
687
  reconnect: z.ZodOptional<z.ZodString>;
688
+ newConversation: z.ZodOptional<z.ZodString>;
685
689
  retry: z.ZodOptional<z.ZodString>;
686
690
  unavailable: z.ZodOptional<z.ZodString>;
687
691
  sessionExpired: z.ZodOptional<z.ZodString>;
@@ -776,12 +780,14 @@ export declare const manifestV2Schema: z.ZodObject<{
776
780
  ltr: "ltr";
777
781
  rtl: "rtl";
778
782
  }>>;
779
- model: z.ZodObject<{
783
+ model: z.ZodUnion<readonly [z.ZodObject<{
780
784
  kind: z.ZodLiteral<"openai-compatible">;
781
785
  baseUrl: z.ZodString;
782
786
  model: z.ZodString;
783
787
  apiKey: z.ZodString;
784
- }, z.core.$strict>;
788
+ }, z.core.$strict>, z.ZodObject<{
789
+ kind: z.ZodLiteral<"noodle-managed">;
790
+ }, z.core.$strict>]>;
785
791
  allowedOrigins: z.ZodArray<z.ZodString>;
786
792
  surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
787
793
  mode: z.ZodEnum<{
@@ -1343,6 +1349,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1343
1349
  copy: z.ZodOptional<z.ZodString>;
1344
1350
  newMessages: z.ZodOptional<z.ZodString>;
1345
1351
  reconnect: z.ZodOptional<z.ZodString>;
1352
+ newConversation: z.ZodOptional<z.ZodString>;
1346
1353
  retry: z.ZodOptional<z.ZodString>;
1347
1354
  unavailable: z.ZodOptional<z.ZodString>;
1348
1355
  sessionExpired: z.ZodOptional<z.ZodString>;
@@ -1437,12 +1444,14 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1437
1444
  ltr: "ltr";
1438
1445
  rtl: "rtl";
1439
1446
  }>>;
1440
- model: z.ZodObject<{
1447
+ model: z.ZodUnion<readonly [z.ZodObject<{
1441
1448
  kind: z.ZodLiteral<"openai-compatible">;
1442
1449
  baseUrl: z.ZodString;
1443
1450
  model: z.ZodString;
1444
1451
  apiKey: z.ZodString;
1445
- }, z.core.$strict>;
1452
+ }, z.core.$strict>, z.ZodObject<{
1453
+ kind: z.ZodLiteral<"noodle-managed">;
1454
+ }, z.core.$strict>]>;
1446
1455
  allowedOrigins: z.ZodArray<z.ZodString>;
1447
1456
  surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
1448
1457
  mode: z.ZodEnum<{
@@ -1793,6 +1802,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1793
1802
  copy: z.ZodOptional<z.ZodString>;
1794
1803
  newMessages: z.ZodOptional<z.ZodString>;
1795
1804
  reconnect: z.ZodOptional<z.ZodString>;
1805
+ newConversation: z.ZodOptional<z.ZodString>;
1796
1806
  retry: z.ZodOptional<z.ZodString>;
1797
1807
  unavailable: z.ZodOptional<z.ZodString>;
1798
1808
  sessionExpired: z.ZodOptional<z.ZodString>;
@@ -1887,12 +1897,14 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1887
1897
  ltr: "ltr";
1888
1898
  rtl: "rtl";
1889
1899
  }>>;
1890
- model: z.ZodObject<{
1900
+ model: z.ZodUnion<readonly [z.ZodObject<{
1891
1901
  kind: z.ZodLiteral<"openai-compatible">;
1892
1902
  baseUrl: z.ZodString;
1893
1903
  model: z.ZodString;
1894
1904
  apiKey: z.ZodString;
1895
- }, z.core.$strict>;
1905
+ }, z.core.$strict>, z.ZodObject<{
1906
+ kind: z.ZodLiteral<"noodle-managed">;
1907
+ }, z.core.$strict>]>;
1896
1908
  allowedOrigins: z.ZodArray<z.ZodString>;
1897
1909
  surfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
1898
1910
  mode: z.ZodEnum<{
@@ -310,6 +310,7 @@ const assistantUiSchema = z
310
310
  copy: z.string().trim().min(1).max(80).optional(),
311
311
  newMessages: z.string().trim().min(1).max(80).optional(),
312
312
  reconnect: z.string().trim().min(1).max(80).optional(),
313
+ newConversation: z.string().trim().min(1).max(80).optional(),
313
314
  retry: z.string().trim().min(1).max(80).optional(),
314
315
  unavailable: z.string().trim().min(1).max(240).optional(),
315
316
  sessionExpired: z.string().trim().min(1).max(240).optional(),
@@ -343,14 +344,17 @@ const MAX_SERVER_INSTRUCTIONS = 4000;
343
344
  const sessionClaimsSchema = z.record(z.string().regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/, 'claim keys are identifier-style'), z.object({ exposeToModel: z.boolean().optional() }).strict());
344
345
  const embeddedAssistantSchema = z
345
346
  .object({
346
- model: z
347
- .object({
348
- kind: z.literal('openai-compatible'),
349
- baseUrl: z.string().trim().min(1),
350
- model: z.string().trim().min(1),
351
- apiKey: z.string().regex(/^[A-Za-z0-9_]+$/),
352
- })
353
- .strict(),
347
+ model: z.union([
348
+ z
349
+ .object({
350
+ kind: z.literal('openai-compatible'),
351
+ baseUrl: z.string().trim().min(1),
352
+ model: z.string().trim().min(1),
353
+ apiKey: z.string().regex(/^[A-Za-z0-9_]+$/),
354
+ })
355
+ .strict(),
356
+ z.object({ kind: z.literal('noodle-managed') }).strict(),
357
+ ]),
354
358
  allowedOrigins: z.array(assistantOriginOrVariableSchema).min(1),
355
359
  // The front doors this assistant serves (ADR 0201, amended 2026-08-12). Absent means the pre-0201
356
360
  // authenticated shape, so released artifacts keep their meaning; `allowedOrigins` stays the union
@@ -4,5 +4,7 @@ export { InMemoryControlPlaneStore } from './in-memory-control-plane-store.js';
4
4
  export { bindInMemoryOrganizationStore, type InMemoryOrganizationOperations, InMemoryOrganizationStore, } from './in-memory-organization-store.js';
5
5
  export { InMemoryMcpSubdomainClaimStore, McpSubdomainCooldownError, McpSubdomainIdempotencyConflictError, McpSubdomainOwnerRequiredError, type McpSubdomainRouteRef, McpSubdomainUnavailableError, mcpSubdomainEndpointOptions, type ResolvedMcpTenantRef, resolveMcpSubdomainTenant, } from './mcp-subdomain-claims.js';
6
6
  export { domainFromEmail, domainKey, memberKey, normalizeSignupAllowlistValue, personalOrgSlug, personalOrgSlugSuffix, signupKey, } from './organization-helpers.js';
7
+ export { type RollbackControlPlane, type RollbackOperationDependencies, type RollbackOperationInput, type RollbackOperationResult, type RollbackRegistry, rollbackDeploymentOperation, } from './rollback-deployment.js';
8
+ export type { RollbackResult } from './rollback-result.js';
7
9
  export { DOMAIN_PATTERN, isSystemOwnedOrgSlug, OPENAI_APPS_CHALLENGE_MAX_LENGTH, SLUG_PATTERN, validateDomain, validateMcpSubdomain, validateOpenAIAppsChallenge, validateOrgMembershipDomain, validateOrgRole, validateSignupAllowlistKind, validateSlug, validateUserOwnedOrgSlug, } from './validation.js';
8
10
  //# sourceMappingURL=portable.d.ts.map
@@ -3,5 +3,6 @@ export { InMemoryControlPlaneStore } from './in-memory-control-plane-store.js';
3
3
  export { bindInMemoryOrganizationStore, InMemoryOrganizationStore, } from './in-memory-organization-store.js';
4
4
  export { InMemoryMcpSubdomainClaimStore, McpSubdomainCooldownError, McpSubdomainIdempotencyConflictError, McpSubdomainOwnerRequiredError, McpSubdomainUnavailableError, mcpSubdomainEndpointOptions, resolveMcpSubdomainTenant, } from './mcp-subdomain-claims.js';
5
5
  export { domainFromEmail, domainKey, memberKey, normalizeSignupAllowlistValue, personalOrgSlug, personalOrgSlugSuffix, signupKey, } from './organization-helpers.js';
6
+ export { rollbackDeploymentOperation, } from './rollback-deployment.js';
6
7
  export { DOMAIN_PATTERN, isSystemOwnedOrgSlug, OPENAI_APPS_CHALLENGE_MAX_LENGTH, SLUG_PATTERN, validateDomain, validateMcpSubdomain, validateOpenAIAppsChallenge, validateOrgMembershipDomain, validateOrgRole, validateSignupAllowlistKind, validateSlug, validateUserOwnedOrgSlug, } from './validation.js';
7
8
  //# sourceMappingURL=portable.js.map
@@ -0,0 +1,61 @@
1
+ import { type AuditSink, type DeploymentOperationTarget, type EndpointUrlOptions } from '@noodle-borg/module';
2
+ import type { RollbackResult } from './rollback-result.js';
3
+ export interface RollbackRegistry {
4
+ getAppArchivedAt(org: string, app: string): Promise<string | undefined>;
5
+ rollback(target: DeploymentOperationTarget, deploymentId: string): Promise<RollbackResult>;
6
+ }
7
+ export interface RollbackControlPlane {
8
+ getOrgMember(input: {
9
+ readonly org: string;
10
+ readonly subject: string;
11
+ }): Promise<{
12
+ readonly role: string;
13
+ } | undefined>;
14
+ }
15
+ export interface RollbackOperationDependencies {
16
+ readonly registry: RollbackRegistry;
17
+ readonly controlPlane: RollbackControlPlane;
18
+ readonly audit: AuditSink;
19
+ }
20
+ export interface RollbackOperationInput {
21
+ readonly actor: {
22
+ readonly subject: string;
23
+ readonly email?: string;
24
+ readonly superAdmin: boolean;
25
+ };
26
+ readonly target: DeploymentOperationTarget;
27
+ readonly deploymentId: string;
28
+ readonly reason?: string;
29
+ readonly publicBaseUrl: string;
30
+ readonly endpointOptions?: EndpointUrlOptions;
31
+ }
32
+ export type RollbackOperationResult = {
33
+ readonly ok: true;
34
+ readonly view: {
35
+ readonly target: DeploymentOperationTarget;
36
+ readonly rollback: {
37
+ readonly deploymentId: string;
38
+ readonly serverVersion?: string;
39
+ readonly previousDeploymentId?: string;
40
+ readonly alreadyActive: boolean;
41
+ readonly endpointUrl: string;
42
+ readonly accessMode: string;
43
+ readonly ownerSubject?: string;
44
+ readonly previousAccessMode?: string;
45
+ readonly serverName: string;
46
+ readonly createdAt: string;
47
+ };
48
+ };
49
+ } | {
50
+ readonly ok: false;
51
+ readonly status: 403 | 404 | 409 | 503;
52
+ readonly code: 'forbidden' | 'app_archived' | 'deployment_not_found' | 'deployment_incompatible' | 'deployment_locked' | 'production_app_limit_exceeded' | 'billing_enforcement_unavailable';
53
+ readonly message: string;
54
+ };
55
+ /**
56
+ * Activate a previous deployment through the service's canonical governance boundary.
57
+ * HTTP routes, agent tools, and MCP App widgets must all call this operation rather than
58
+ * duplicating owner checks, commercial admission, endpoint construction, or audit behavior.
59
+ */
60
+ export declare function rollbackDeploymentOperation(dependencies: RollbackOperationDependencies, input: RollbackOperationInput): Promise<RollbackOperationResult>;
61
+ //# sourceMappingURL=rollback-deployment.d.ts.map
@@ -1,5 +1,4 @@
1
- import { DeploymentActivationError } from '@noodle-borg/module';
2
- import { tenantMcpUrl } from '../registry-helpers.js';
1
+ import { DeploymentActivationError, tenantMcpUrl, } from '@noodle-borg/module';
3
2
  /**
4
3
  * Activate a previous deployment through the service's canonical governance boundary.
5
4
  * HTTP routes, agent tools, and MCP App widgets must all call this operation rather than
@@ -81,6 +80,7 @@ export async function rollbackDeploymentOperation(dependencies, input) {
81
80
  : {}),
82
81
  alreadyActive: result.alreadyActive,
83
82
  accessMode: result.accessMode,
83
+ ...(result.ownerSubject !== undefined ? { ownerSubject: result.ownerSubject } : {}),
84
84
  ...(result.previousAccessMode !== undefined
85
85
  ? { previousAccessMode: result.previousAccessMode }
86
86
  : {}),
@@ -101,6 +101,7 @@ export async function rollbackDeploymentOperation(dependencies, input) {
101
101
  alreadyActive: result.alreadyActive,
102
102
  endpointUrl,
103
103
  accessMode: result.accessMode,
104
+ ...(result.ownerSubject !== undefined ? { ownerSubject: result.ownerSubject } : {}),
104
105
  ...(result.previousAccessMode !== undefined
105
106
  ? { previousAccessMode: result.previousAccessMode }
106
107
  : {}),
@@ -0,0 +1,19 @@
1
+ import type { AccessMode } from '@noodle-borg/module';
2
+ export type RollbackResult = {
3
+ readonly ok: true;
4
+ readonly deploymentId: string;
5
+ readonly serverVersion?: string;
6
+ readonly previousDeploymentId?: string;
7
+ readonly alreadyActive: boolean;
8
+ readonly accessMode: AccessMode;
9
+ readonly ownerSubject?: string;
10
+ readonly previousAccessMode?: AccessMode;
11
+ readonly serverName: string;
12
+ readonly createdAt: string;
13
+ } | {
14
+ readonly ok: false;
15
+ readonly status: 404 | 409;
16
+ readonly code?: 'deployment_locked';
17
+ readonly error: string;
18
+ };
19
+ //# sourceMappingURL=rollback-result.d.ts.map
@@ -25,7 +25,9 @@
25
25
  "build": "tsc -p tsconfig.json",
26
26
  "typecheck": "tsc -p tsconfig.json --noEmit"
27
27
  },
28
- "dependencies": {},
28
+ "dependencies": {
29
+ "@noodle-borg/module": "0.0.0"
30
+ },
29
31
  "devDependencies": {
30
32
  "@types/node": "^24.13.1",
31
33
  "@types/pg": "^8.20.0"
@@ -23,6 +23,7 @@ export const tenantSlugSchema = z
23
23
  export const appSlugSchema = tenantSlugSchema;
24
24
  export const environmentNameSchema = tenantSlugSchema;
25
25
  export const boundedIdSchema = z.string().min(1).max(200);
26
+ const ownerSubjectSchema = z.string().min(1).max(512);
26
27
  export const developerMcpContextSchema = z.strictObject({
27
28
  subject: z.string().min(1).max(255),
28
29
  clientId: z.string().min(1).max(255),
@@ -122,6 +123,7 @@ export const deploymentSummarySchema = z.strictObject({
122
123
  serverName: z.string().min(1).max(200),
123
124
  createdAt: isoTimestampSchema,
124
125
  accessMode: z.string().min(1).max(64).optional(),
126
+ ownerSubject: ownerSubjectSchema.optional(),
125
127
  endpointUrl: z.url().optional(),
126
128
  });
127
129
  export const appSummaryViewSchema = z.strictObject({
@@ -163,6 +165,7 @@ export const deploymentInspectionViewSchema = z.strictObject({
163
165
  createdAt: isoTimestampSchema,
164
166
  createdByEmail: z.email().optional(),
165
167
  accessMode: z.string().min(1).max(64),
168
+ ownerSubject: ownerSubjectSchema.optional(),
166
169
  }),
167
170
  health: z.strictObject({
168
171
  state: z.string().min(1).max(100),
@@ -203,6 +206,7 @@ export const rollbackViewSchema = z.strictObject({
203
206
  alreadyActive: z.boolean(),
204
207
  endpointUrl: z.url(),
205
208
  accessMode: z.string().min(1).max(64),
209
+ ownerSubject: ownerSubjectSchema.optional(),
206
210
  previousAccessMode: z.string().min(1).max(64).optional(),
207
211
  serverName: z.string().min(1).max(200),
208
212
  createdAt: isoTimestampSchema,
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ActionStatus, BoundValue, EmptyState, Panel, WidgetHeader } from './shared.js';
3
3
  export function AppOverviewWidget() {
4
- return (_jsxs("main", { children: [_jsx(WidgetHeader, { eyebrow: "Noodle Cloud", title: "App overview", description: "Deployment state for the organization and environments selected at connection time.", statusPath: "data.active" }), _jsx(Panel, { title: "Selected app", description: "This view is limited to the active developer grant.", children: _jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "App", path: "data.app" }), _jsx(BoundValue, { label: "Environment", path: "data.selectedEnvironment" }), _jsx(BoundValue, { label: "Active", path: "data.active" }), _jsx(BoundValue, { label: "Last activity", path: "data.lastActivityAt" })] }) }), _jsx(Panel, { title: "Granted environments", children: _jsxs("div", { className: "tag-list", "data-collection": "data.environments", "data-collection-item": "environment", children: [_jsx("template", { "data-collection-template": "", children: _jsx("span", { className: "tag", "data-bind": "environment", children: "Loading" }) }), _jsx(EmptyState, { children: "No environment is available in this result." })] }) }), _jsxs(Panel, { title: "Active deployment", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "Deployment", path: "data.latest.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.latest.createdAt" }), _jsx(BoundValue, { label: "Access", path: "data.latest.accessMode" }), _jsx(BoundValue, { label: "Endpoint", path: "data.latest.endpointUrl" })] }), _jsx("p", { className: "notice", "data-bind-if": "data.active", children: "An active deployment is serving this environment." })] }), _jsx(ActionStatus, {})] }));
4
+ return (_jsxs("main", { children: [_jsx(WidgetHeader, { eyebrow: "Noodle Cloud", title: "App overview", description: "Deployment state for the organization and environments selected at connection time.", statusPath: "data.active" }), _jsx(Panel, { title: "Selected app", description: "This view is limited to the active developer grant.", children: _jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "App", path: "data.app" }), _jsx(BoundValue, { label: "Environment", path: "data.selectedEnvironment" }), _jsx(BoundValue, { label: "Active", path: "data.active" }), _jsx(BoundValue, { label: "Last activity", path: "data.lastActivityAt" })] }) }), _jsx(Panel, { title: "Granted environments", children: _jsxs("div", { className: "tag-list", "data-collection": "data.environments", "data-collection-item": "environment", children: [_jsx("template", { "data-collection-template": "", children: _jsx("span", { className: "tag", "data-bind": "environment", children: "Loading" }) }), _jsx(EmptyState, { children: "No environment is available in this result." })] }) }), _jsxs(Panel, { title: "Active deployment", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "Deployment", path: "data.latest.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.latest.createdAt" }), _jsx(BoundValue, { label: "Access", path: "data.latest.accessMode" }), _jsx(BoundValue, { label: "Owner", path: "data.latest.ownerSubject" }), _jsx(BoundValue, { label: "Endpoint", path: "data.latest.endpointUrl" })] }), _jsx("p", { className: "notice", "data-bind-if": "data.active", children: "An active deployment is serving this environment." })] }), _jsx(ActionStatus, {})] }));
5
5
  }
6
6
  //# sourceMappingURL=app-overview.js.map
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ActionStatus, BoundValue, EmptyState, Panel, WidgetHeader } from './shared.js';
3
3
  const ROLLBACK_ARGUMENTS = '{"app":"{{data.target.app}}","env":"{{data.target.env}}","deploymentId":"{{data.rollbackCandidate.deploymentId}}"}';
4
4
  export function DeploymentDetailWidget() {
5
- return (_jsxs("main", { children: [_jsx(WidgetHeader, { eyebrow: "Noodle Cloud", title: "Deployment detail", description: "Runtime health, surface compatibility, and governed rollback state.", statusPath: "data.health.state" }), _jsx(Panel, { title: "Deployment identity", children: _jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "App", path: "data.target.app" }), _jsx(BoundValue, { label: "Environment", path: "data.target.env" }), _jsx(BoundValue, { label: "Deployment", path: "data.deployment.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.deployment.createdAt" }), _jsx(BoundValue, { label: "Access", path: "data.deployment.accessMode" }), _jsx(BoundValue, { label: "Endpoint", path: "data.deployment.endpointUrl" })] }) }), _jsxs(Panel, { title: "Runtime health", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "State", path: "data.health.state" }), _jsx(BoundValue, { label: "MCP Apps", path: "data.surface.compatibility.mcpApps" }), _jsx(BoundValue, { label: "ChatGPT", path: "data.surface.compatibility.chatgpt" }), _jsx(BoundValue, { label: "Claude", path: "data.surface.compatibility.claude" })] }), _jsxs("div", { className: "collection", "data-collection": "data.health.missingSecrets", "data-collection-item": "missing", children: [_jsx("template", { "data-collection-template": "", children: _jsx("p", { className: "collection-card", "data-bind": "missing", children: "Loading" }) }), _jsx(EmptyState, { children: "No missing managed configuration was reported." })] })] }), _jsxs(Panel, { title: "Rollback", description: "Noodle Cloud rechecks ownership, selected environment, compatibility, and admission before activation.", children: [_jsxs("div", { className: "action-row", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "Candidate", path: "data.rollbackCandidate.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.rollbackCandidate.createdAt" })] }), _jsx("button", { type: "button", className: "primary-action danger-action", "data-action": "call", "data-action-tool": "rollback_deployment", "data-action-args": ROLLBACK_ARGUMENTS, "data-bind-if": "data.rollbackCandidate.deploymentId", hidden: true, children: "Roll back to candidate" })] }), _jsx("p", { className: "notice", children: "If no candidate is shown, use the headless deployment workflow to inspect an explicit deployment ID." })] }), _jsx(Panel, { title: "Rollback outcome", children: _jsxs("dl", { className: "facts", "data-bind-if": "data.rollback.deploymentId", children: [_jsx(BoundValue, { label: "Activated deployment", path: "data.rollback.deploymentId" }), _jsx(BoundValue, { label: "Previously active", path: "data.rollback.previousDeploymentId" }), _jsx(BoundValue, { label: "Already active", path: "data.rollback.alreadyActive" }), _jsx(BoundValue, { label: "Endpoint", path: "data.rollback.endpointUrl" })] }) }), _jsx(ActionStatus, {})] }));
5
+ return (_jsxs("main", { children: [_jsx(WidgetHeader, { eyebrow: "Noodle Cloud", title: "Deployment detail", description: "Runtime health, surface compatibility, and governed rollback state.", statusPath: "data.health.state" }), _jsx(Panel, { title: "Deployment identity", children: _jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "App", path: "data.target.app" }), _jsx(BoundValue, { label: "Environment", path: "data.target.env" }), _jsx(BoundValue, { label: "Deployment", path: "data.deployment.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.deployment.createdAt" }), _jsx(BoundValue, { label: "Access", path: "data.deployment.accessMode" }), _jsx(BoundValue, { label: "Owner", path: "data.deployment.ownerSubject" }), _jsx(BoundValue, { label: "Endpoint", path: "data.deployment.endpointUrl" })] }) }), _jsxs(Panel, { title: "Runtime health", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "State", path: "data.health.state" }), _jsx(BoundValue, { label: "MCP Apps", path: "data.surface.compatibility.mcpApps" }), _jsx(BoundValue, { label: "ChatGPT", path: "data.surface.compatibility.chatgpt" }), _jsx(BoundValue, { label: "Claude", path: "data.surface.compatibility.claude" })] }), _jsxs("div", { className: "collection", "data-collection": "data.health.missingSecrets", "data-collection-item": "missing", children: [_jsx("template", { "data-collection-template": "", children: _jsx("p", { className: "collection-card", "data-bind": "missing", children: "Loading" }) }), _jsx(EmptyState, { children: "No missing managed configuration was reported." })] })] }), _jsxs(Panel, { title: "Rollback", description: "Noodle Cloud rechecks ownership, selected environment, compatibility, and admission before activation.", children: [_jsxs("div", { className: "action-row", children: [_jsxs("dl", { className: "facts", children: [_jsx(BoundValue, { label: "Candidate", path: "data.rollbackCandidate.deploymentId" }), _jsx(BoundValue, { label: "Created", path: "data.rollbackCandidate.createdAt" })] }), _jsx("button", { type: "button", className: "primary-action danger-action", "data-action": "call", "data-action-tool": "rollback_deployment", "data-action-args": ROLLBACK_ARGUMENTS, "data-bind-if": "data.rollbackCandidate.deploymentId", hidden: true, children: "Roll back to candidate" })] }), _jsx("p", { className: "notice", children: "If no candidate is shown, use the headless deployment workflow to inspect an explicit deployment ID." })] }), _jsx(Panel, { title: "Rollback outcome", children: _jsxs("dl", { className: "facts", "data-bind-if": "data.rollback.deploymentId", children: [_jsx(BoundValue, { label: "Activated deployment", path: "data.rollback.deploymentId" }), _jsx(BoundValue, { label: "Previously active", path: "data.rollback.previousDeploymentId" }), _jsx(BoundValue, { label: "Already active", path: "data.rollback.alreadyActive" }), _jsx(BoundValue, { label: "Endpoint", path: "data.rollback.endpointUrl" }), _jsx(BoundValue, { label: "Owner", path: "data.rollback.ownerSubject" })] }) }), _jsx(ActionStatus, {})] }));
6
6
  }
7
7
  //# sourceMappingURL=deployment-detail.js.map
@@ -399,6 +399,7 @@ export interface RequestEventFilter {
399
399
  readonly org: string;
400
400
  readonly app?: string;
401
401
  readonly env?: string;
402
+ readonly method?: string;
402
403
  readonly outcome?: RequestOutcome;
403
404
  readonly toolName?: string;
404
405
  readonly clientName?: string;
@@ -16,7 +16,6 @@ export type DeploymentAutomationInput = {
16
16
  readonly previousAccessMode?: AccessMode;
17
17
  readonly declaredAccessMode?: string;
18
18
  readonly previousOwnerSubject?: string;
19
- readonly previousOwnerEmail?: string;
20
19
  };
21
20
  export type DeploymentAutomationAuthorization = {
22
21
  readonly kind: 'not-automation';
@@ -29,6 +28,7 @@ export type DeploymentAutomationAuthorization = {
29
28
  readonly kind: 'authorized';
30
29
  readonly automationId: string;
31
30
  readonly actor: ControlPlaneIdentity;
31
+ readonly ownerSubject?: string;
32
32
  readonly accessMode?: AccessMode;
33
33
  readonly accessModeSource?: 'previous' | 'declared';
34
34
  };
@@ -0,0 +1,11 @@
1
+ export interface DeploymentOperationTarget {
2
+ readonly org: string;
3
+ readonly app: string;
4
+ readonly env: string;
5
+ }
6
+ export interface EndpointUrlOptions {
7
+ readonly publicBaseDomain?: string;
8
+ readonly mcpSubdomain?: string;
9
+ }
10
+ export declare function tenantMcpUrl(base: string, tenant: DeploymentOperationTarget, serverVersion?: string, options?: EndpointUrlOptions): string;
11
+ //# sourceMappingURL=endpoint-url.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { formatPublicMcpUrl, serverVersionPathSegment } from './contract.js';
2
+ export function tenantMcpUrl(base, tenant, serverVersion, options = {}) {
3
+ if (options.publicBaseDomain !== undefined) {
4
+ if (options.mcpSubdomain === undefined) {
5
+ throw new Error('MCP subdomain is required to format a public endpoint URL');
6
+ }
7
+ return formatPublicMcpUrl(options.publicBaseDomain, {
8
+ mcpSubdomain: options.mcpSubdomain,
9
+ app: tenant.app,
10
+ env: tenant.env,
11
+ ...(serverVersion !== undefined ? { serverVersion } : {}),
12
+ });
13
+ }
14
+ const versionPath = serverVersion !== undefined ? `/${serverVersionPathSegment(serverVersion)}` : '';
15
+ const origin = new URL(base).origin;
16
+ return tenant.env === 'prod'
17
+ ? `${origin}/o/${tenant.org}/${tenant.app}${versionPath}/mcp`
18
+ : `${origin}/o/${tenant.org}/${tenant.app}/${tenant.env}${versionPath}/mcp`;
19
+ }
20
+ //# sourceMappingURL=endpoint-url.js.map
@@ -2,6 +2,7 @@ export * from './asset-store.js';
2
2
  export * from './contract.js';
3
3
  export * from './deployment-activation.js';
4
4
  export * from './deployment-automation.js';
5
+ export * from './endpoint-url.js';
5
6
  export * from './intent.js';
6
7
  export * from './organization-provisioning.js';
7
8
  export * from './platform-human-identity.js';
@@ -2,6 +2,7 @@ export * from './asset-store.js';
2
2
  export * from './contract.js';
3
3
  export * from './deployment-activation.js';
4
4
  export * from './deployment-automation.js';
5
+ export * from './endpoint-url.js';
5
6
  export * from './intent.js';
6
7
  export * from './organization-provisioning.js';
7
8
  export * from './platform-human-identity.js';
@@ -0,0 +1,217 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ export const ASSISTANT_USAGE_METHOD = 'assistant';
3
+ const EMPTY_TURN_COUNTERS = {
4
+ modelRequests: 0,
5
+ toolCalls: 0,
6
+ interactionCount: 0,
7
+ promptTokens: 0,
8
+ completionTokens: 0,
9
+ totalTokens: 0,
10
+ };
11
+ export function assistantSessionUsageRequestEvent(session, durationMs) {
12
+ return assistantUsageRequestEvent({
13
+ ...session.tenant,
14
+ deploymentId: session.deploymentId,
15
+ eventKind: 'session',
16
+ sessionId: session.id,
17
+ surface: assistantSurface(session),
18
+ ...(session.modelSource === undefined ? {} : { modelSource: session.modelSource }),
19
+ durationMs,
20
+ });
21
+ }
22
+ export function assistantTurnUsageRequestEvent(session, input) {
23
+ return assistantUsageRequestEvent({
24
+ ...session.tenant,
25
+ deploymentId: session.deploymentId,
26
+ eventKind: 'turn',
27
+ sessionId: session.id,
28
+ surface: assistantSurface(session),
29
+ ...(session.modelSource === undefined ? {} : { modelSource: session.modelSource }),
30
+ outcome: input.outcome,
31
+ turnNumber: input.turnNumber,
32
+ durationMs: input.durationMs,
33
+ ...(input.counters ?? EMPTY_TURN_COUNTERS),
34
+ ...(input.errorKind === undefined ? {} : { errorKind: input.errorKind }),
35
+ });
36
+ }
37
+ export function assistantRefusedTurnUsageRequestEvent(session, errorKind, durationMs) {
38
+ return assistantTurnUsageRequestEvent(session, {
39
+ outcome: 'refused',
40
+ turnNumber: session.turnCount,
41
+ durationMs,
42
+ errorKind,
43
+ });
44
+ }
45
+ function assistantSurface(session) {
46
+ return session.publicEmbedId === undefined ? 'authenticated' : 'public';
47
+ }
48
+ export function assistantTurnNumber(session) {
49
+ if (session.publicEmbedId !== undefined)
50
+ return session.turnCount + 1;
51
+ return session.history.filter((message) => message.role === 'user').length + 1;
52
+ }
53
+ export function captureAssistantUsage(capture, event) {
54
+ try {
55
+ capture?.(event);
56
+ }
57
+ catch {
58
+ // Telemetry is best effort and never changes the assistant response path.
59
+ }
60
+ }
61
+ /**
62
+ * Project one assistant lifecycle event onto the shared tenant request stream. The allowlist is
63
+ * deliberately scalar-only: conversation text, model messages, tool arguments/results, headers,
64
+ * credentials, and page context have no field to enter through.
65
+ */
66
+ export function assistantUsageRequestEvent(input) {
67
+ const details = {
68
+ eventKind: input.eventKind,
69
+ surface: input.surface,
70
+ ...(input.modelSource === undefined ? {} : { modelSource: input.modelSource }),
71
+ ...(input.eventKind === 'turn'
72
+ ? {
73
+ assistantOutcome: input.outcome,
74
+ turnNumber: input.turnNumber,
75
+ modelRequests: input.modelRequests,
76
+ toolCalls: input.toolCalls,
77
+ interactionCount: input.interactionCount,
78
+ promptTokens: input.promptTokens,
79
+ completionTokens: input.completionTokens,
80
+ totalTokens: input.totalTokens,
81
+ ...(input.reasoningTokens === undefined
82
+ ? {}
83
+ : { reasoningTokens: input.reasoningTokens }),
84
+ }
85
+ : {}),
86
+ };
87
+ return {
88
+ org: input.org,
89
+ app: input.app,
90
+ env: input.env,
91
+ deploymentId: input.deploymentId,
92
+ requestId: randomUUID(),
93
+ sessionId: input.sessionId,
94
+ sessionSource: 'synthetic',
95
+ subjectKind: input.surface === 'public' ? 'anonymous' : 'authenticated',
96
+ accessMode: input.surface === 'public' ? 'public' : 'authenticated',
97
+ method: ASSISTANT_USAGE_METHOD,
98
+ kind: 'usage',
99
+ outcome: input.eventKind === 'session' || input.outcome === 'delivered'
100
+ ? 'ok'
101
+ : input.outcome === 'refused'
102
+ ? 'tool_error'
103
+ : 'mcp_error',
104
+ ...(input.eventKind === 'turn' && input.errorKind !== undefined
105
+ ? { errorKind: input.errorKind }
106
+ : {}),
107
+ durationMs: finiteNonNegative(input.durationMs),
108
+ details,
109
+ };
110
+ }
111
+ export function aggregateAssistantUsage(events) {
112
+ let minted = 0;
113
+ let publicSessions = 0;
114
+ let authenticatedSessions = 0;
115
+ let delivered = 0;
116
+ let failed = 0;
117
+ let refused = 0;
118
+ let modelRequests = 0;
119
+ let toolTurns = 0;
120
+ let interactionTurns = 0;
121
+ let prompt = 0;
122
+ let completion = 0;
123
+ let reasoning = 0;
124
+ let total = 0;
125
+ let noodleManaged = 0;
126
+ let operator = 0;
127
+ const latencies = [];
128
+ const sessionDepth = new Map();
129
+ for (const event of events) {
130
+ if (event.method !== ASSISTANT_USAGE_METHOD)
131
+ continue;
132
+ const details = event.details;
133
+ const eventKind = scalarString(details?.eventKind);
134
+ const surface = scalarString(details?.surface);
135
+ if (eventKind === 'session') {
136
+ minted += 1;
137
+ if (surface === 'public')
138
+ publicSessions += 1;
139
+ if (surface === 'authenticated')
140
+ authenticatedSessions += 1;
141
+ continue;
142
+ }
143
+ if (eventKind !== 'turn')
144
+ continue;
145
+ const outcome = scalarString(details?.assistantOutcome);
146
+ if (outcome === 'delivered')
147
+ delivered += 1;
148
+ else if (outcome === 'refused')
149
+ refused += 1;
150
+ else if (outcome === 'failed')
151
+ failed += 1;
152
+ else
153
+ continue;
154
+ latencies.push(finiteNonNegative(event.durationMs));
155
+ modelRequests += scalarCount(details?.modelRequests);
156
+ if (scalarCount(details?.toolCalls) > 0)
157
+ toolTurns += 1;
158
+ if (scalarCount(details?.interactionCount) > 0)
159
+ interactionTurns += 1;
160
+ prompt += scalarCount(details?.promptTokens);
161
+ completion += scalarCount(details?.completionTokens);
162
+ reasoning += scalarCount(details?.reasoningTokens);
163
+ total += scalarCount(details?.totalTokens);
164
+ const source = scalarString(details?.modelSource);
165
+ if (source === 'noodle-managed')
166
+ noodleManaged += 1;
167
+ if (source === 'operator')
168
+ operator += 1;
169
+ const turnNumber = scalarCount(details?.turnNumber);
170
+ if (event.sessionId !== undefined && turnNumber > 0) {
171
+ sessionDepth.set(event.sessionId, Math.max(sessionDepth.get(event.sessionId) ?? 0, turnNumber));
172
+ }
173
+ }
174
+ const depths = [...sessionDepth.values()].sort((a, b) => a - b);
175
+ const sortedLatencies = latencies.sort((a, b) => a - b);
176
+ return {
177
+ sessions: { minted, public: publicSessions, authenticated: authenticatedSessions },
178
+ turns: {
179
+ attempted: delivered + failed + refused,
180
+ delivered,
181
+ failed,
182
+ refused,
183
+ },
184
+ depth: {
185
+ p50: percentile(depths, 50),
186
+ p90: percentile(depths, 90),
187
+ max: depths.at(-1) ?? 0,
188
+ atLeast10: depths.filter((value) => value >= 10).length,
189
+ atLeast20: depths.filter((value) => value >= 20).length,
190
+ atLeast30: depths.filter((value) => value >= 30).length,
191
+ atLeast40: depths.filter((value) => value >= 40).length,
192
+ },
193
+ engagement: { modelRequests, toolTurns, interactionTurns },
194
+ latency: {
195
+ p50Ms: percentile(sortedLatencies, 50),
196
+ p95Ms: percentile(sortedLatencies, 95),
197
+ },
198
+ tokens: { prompt, completion, reasoning, total },
199
+ byModelSource: { noodleManaged, operator },
200
+ };
201
+ }
202
+ function scalarString(value) {
203
+ return typeof value === 'string' ? value : undefined;
204
+ }
205
+ function scalarCount(value) {
206
+ return typeof value === 'number' ? Math.floor(finiteNonNegative(value)) : 0;
207
+ }
208
+ function finiteNonNegative(value) {
209
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
210
+ }
211
+ function percentile(sorted, percentileValue) {
212
+ if (sorted.length === 0)
213
+ return 0;
214
+ const rank = Math.ceil((percentileValue / 100) * sorted.length);
215
+ return sorted[Math.min(Math.max(rank, 1), sorted.length) - 1] ?? 0;
216
+ }
217
+ //# sourceMappingURL=assistant-usage.js.map
@@ -1,5 +1,7 @@
1
+ export * from './assistant-usage.js';
1
2
  export * from './intent-capture.js';
2
3
  export * from './request-event-buffer.js';
4
+ export * from './request-event-metrics.js';
3
5
  export * from './request-events.js';
4
6
  export * from './request-events-postgres.js';
5
7
  export * from './runtime.js';