@noodleseed/one 0.151.0 → 0.152.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 (64) hide show
  1. package/dist/assistant-embed-scaffold-template.d.ts +1 -0
  2. package/dist/assistant-embed-scaffold-template.d.ts.map +1 -1
  3. package/dist/assistant-embed-scaffold-template.js +80 -12
  4. package/dist/assistant-embed-scaffold-template.js.map +1 -1
  5. package/dist/commands/assistant-embed-ops.d.ts.map +1 -1
  6. package/dist/commands/assistant-embed-ops.js +141 -4
  7. package/dist/commands/assistant-embed-ops.js.map +1 -1
  8. package/dist/commands/assistant-ops.js +2 -1
  9. package/dist/commands/assistant-ops.js.map +1 -1
  10. package/node_modules/@noodle-borg/agent-kit/dist/behavior-skills.js +10 -10
  11. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +6 -5
  12. package/node_modules/@noodle-borg/agent-kit/dist/index.js +13 -1
  13. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +8 -0
  14. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js +1 -1
  15. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-operations-ref.js +78 -0
  16. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +156 -135
  17. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-renderer-ref.js +109 -0
  18. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  19. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-browser-fields.d.ts +3 -0
  20. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-browser-fields.js +15 -0
  21. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-configuration.d.ts +1 -1
  22. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-configuration.js +1 -1
  23. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.d.ts +19 -11
  24. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.js +24 -23
  25. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-transcript-events.d.ts +24 -0
  26. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-transcript-events.js +39 -0
  27. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-transcript.d.ts +6 -2
  28. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-transcript.js +11 -0
  29. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-view-availability.d.ts +8 -0
  30. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-view-availability.js +8 -0
  31. package/node_modules/@noodle-borg/assistant-gateway/dist/elevation-store.d.ts +3 -0
  32. package/node_modules/@noodle-borg/assistant-gateway/dist/elevation.d.ts +7 -1
  33. package/node_modules/@noodle-borg/assistant-gateway/dist/elevation.js +1 -0
  34. package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-elevation-store.d.ts +1 -0
  35. package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-elevation-store.js +1 -0
  36. package/node_modules/@noodle-borg/assistant-gateway/dist/managed-spend.d.ts +29 -0
  37. package/node_modules/@noodle-borg/assistant-gateway/dist/managed-spend.js +27 -1
  38. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +1 -0
  39. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +1 -0
  40. package/node_modules/@noodle-borg/assistant-gateway/package.json +1 -0
  41. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +2 -0
  42. package/node_modules/@noodle-borg/authoring/dist/server.js +3 -0
  43. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +1 -0
  44. package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +4 -1
  45. package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +4 -1
  46. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +4 -0
  47. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +20 -1
  48. package/node_modules/@noodle-borg/compiler/dist/state-handles.js +1 -0
  49. package/node_modules/@noodle-borg/runtime/dist/index.d.ts +1 -0
  50. package/node_modules/@noodle-borg/runtime/dist/index.js +1 -0
  51. package/node_modules/@noodle-borg/runtime/dist/state-handle-ownership.d.ts +21 -0
  52. package/node_modules/@noodle-borg/runtime/dist/state-handle-ownership.js +10 -0
  53. package/node_modules/@noodle-borg/runtime/package.json +6 -1
  54. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +8 -3
  55. package/node_modules/@noodle-borg/service/dist/routes/assistant-elevation.js +14 -7
  56. package/node_modules/@noodle-borg/service/dist/routes/assistant-interaction-replay.js +4 -2
  57. package/node_modules/@noodle-borg/service/dist/routes/assistant-interactions.js +4 -2
  58. package/node_modules/@noodle-borg/service/dist/routes/assistant-transcript.js +48 -9
  59. package/node_modules/@noodle-borg/service/dist/service.js +3 -0
  60. package/node_modules/@noodle-borg/service/package.json +1 -1
  61. package/node_modules/@noodle-borg/wire-contracts/dist/assistant.d.ts +19 -25
  62. package/node_modules/@noodle-borg/wire-contracts/dist/assistant.js +20 -34
  63. package/node_modules/@noodleseed/assistant/README.md +1 -1
  64. package/package.json +2 -2
@@ -150,6 +150,7 @@ export interface ArtifactStateHandle {
150
150
  readonly version: string;
151
151
  readonly scope: ArtifactStateHandleScope;
152
152
  readonly ttlSeconds?: number;
153
+ readonly claimOnAuthentication?: true;
153
154
  }
154
155
  export interface ArtifactState {
155
156
  readonly handles: Readonly<Record<string, ArtifactStateHandle>>;
@@ -47,8 +47,11 @@
47
47
  * endpoint claim mappings, resolved operation references may carry ordinary/action endpoint dependency
48
48
  * keys, and artifacts carry only endpoint policies. Resolved customer URLs remain request-private runtime
49
49
  * state.
50
+ *
51
+ * `0.16.0` (Anonymous-to-authenticated onboarding): additive — finite caller-scoped state handles may
52
+ * opt into atomic ownership adoption when a single-use assistant sign-in ticket is spent.
50
53
  */
51
- export declare const ARTIFACT_SCHEMA_VERSION = "0.15.0";
54
+ export declare const ARTIFACT_SCHEMA_VERSION = "0.16.0";
52
55
  /** mimeType for an MCP Apps UI resource (SEP-1865). Widgets are served under this profile. */
53
56
  export declare const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
54
57
  //# sourceMappingURL=version.d.ts.map
@@ -47,8 +47,11 @@
47
47
  * endpoint claim mappings, resolved operation references may carry ordinary/action endpoint dependency
48
48
  * keys, and artifacts carry only endpoint policies. Resolved customer URLs remain request-private runtime
49
49
  * state.
50
+ *
51
+ * `0.16.0` (Anonymous-to-authenticated onboarding): additive — finite caller-scoped state handles may
52
+ * opt into atomic ownership adoption when a single-use assistant sign-in ticket is spent.
50
53
  */
51
- export const ARTIFACT_SCHEMA_VERSION = '0.15.0';
54
+ export const ARTIFACT_SCHEMA_VERSION = '0.16.0';
52
55
  /** mimeType for an MCP Apps UI resource (SEP-1865). Widgets are served under this profile. */
53
56
  export const MCP_APP_MIME_TYPE = 'text/html;profile=mcp-app';
54
57
  //# sourceMappingURL=version.js.map
@@ -25,6 +25,7 @@ export declare const manifestV1Schema: z.ZodObject<{
25
25
  caller: "caller";
26
26
  }>>;
27
27
  ttlSeconds: z.ZodOptional<z.ZodNumber>;
28
+ claimOnAuthentication: z.ZodOptional<z.ZodLiteral<true>>;
28
29
  }, z.core.$strict>>;
29
30
  }, z.core.$strict>>;
30
31
  requires: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
@@ -549,6 +550,7 @@ export declare const manifestV2Schema: z.ZodObject<{
549
550
  caller: "caller";
550
551
  }>>;
551
552
  ttlSeconds: z.ZodOptional<z.ZodNumber>;
553
+ claimOnAuthentication: z.ZodOptional<z.ZodLiteral<true>>;
552
554
  }, z.core.$strict>>;
553
555
  }, z.core.$strict>>;
554
556
  requires: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
@@ -1199,6 +1201,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1199
1201
  caller: "caller";
1200
1202
  }>>;
1201
1203
  ttlSeconds: z.ZodOptional<z.ZodNumber>;
1204
+ claimOnAuthentication: z.ZodOptional<z.ZodLiteral<true>>;
1202
1205
  }, z.core.$strict>>;
1203
1206
  }, z.core.$strict>>;
1204
1207
  requires: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
@@ -1722,6 +1725,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1722
1725
  caller: "caller";
1723
1726
  }>>;
1724
1727
  ttlSeconds: z.ZodOptional<z.ZodNumber>;
1728
+ claimOnAuthentication: z.ZodOptional<z.ZodLiteral<true>>;
1725
1729
  }, z.core.$strict>>;
1726
1730
  }, z.core.$strict>>;
1727
1731
  requires: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
@@ -490,8 +490,27 @@ const stateHandleSchema = z
490
490
  .positive()
491
491
  .max(60 * 60 * 24 * 30)
492
492
  .optional(),
493
+ claimOnAuthentication: z.literal(true).optional(),
493
494
  })
494
- .strict();
495
+ .strict()
496
+ .superRefine((handle, ctx) => {
497
+ if (handle.claimOnAuthentication !== true)
498
+ return;
499
+ if (handle.scope !== 'caller') {
500
+ ctx.addIssue({
501
+ code: 'custom',
502
+ path: ['scope'],
503
+ message: 'claimOnAuthentication requires an explicit caller scope',
504
+ });
505
+ }
506
+ if (handle.ttlSeconds === undefined) {
507
+ ctx.addIssue({
508
+ code: 'custom',
509
+ path: ['ttlSeconds'],
510
+ message: 'claimOnAuthentication requires a finite ttlSeconds',
511
+ });
512
+ }
513
+ });
495
514
  const stateSchema = z
496
515
  .object({
497
516
  handles: z
@@ -27,6 +27,7 @@ export function compileState(manifest, schemasMap, errors) {
27
27
  version: handle.version,
28
28
  scope: handle.scope ?? 'deployment',
29
29
  ...(handle.ttlSeconds !== undefined ? { ttlSeconds: handle.ttlSeconds } : {}),
30
+ ...(handle.claimOnAuthentication === true ? { claimOnAuthentication: true } : {}),
30
31
  };
31
32
  }
32
33
  return { handles };
@@ -18,6 +18,7 @@ export { AllowAllPolicy } from './policy/allow-all.js';
18
18
  export type { PolicyContext, PolicyDecision, PolicyGate } from './policy/types.js';
19
19
  export { type ConfirmationActionReview, type ConfirmationPreparationResult, type ConfirmationReview, type ConnectorFailureAttribution, type ElicitationRequest, type ElicitationResponse, type ExecutionError, type ExecutionErrorCode, type ExecutionResult, type InteractiveExecutionResult, isConfirmationRequired, isInputRequired, isInputRequiredForConfirmation, type PreparedOperationAction, type PreparedToolContinuation, type ToolContinuation, type ToolPreparationContinuation, } from './result.js';
20
20
  export { splitResultMeta } from './result-meta.js';
21
+ export { type CallerStateAdoptionInput, type CallerStateAdoptionResult, claimableStateHandleNames, } from './state-handle-ownership.js';
21
22
  export { assertExpectedStateRevision, assertNoSecretValue, assertSchemaValue, assertStateHandleNotCompleted, COMPLETE_STATE_OPERATION, createMutableStateHandleRecord, createStateConnector, InMemoryStateHandleStore, type MutableStateHandleRecord, PATCH_STATE_OPERATION, READ_STATE_OPERATION, STATE_CONNECTOR_ID, STATE_CONNECTOR_VERSION, STATE_OPERATION_SIGNATURES, StateConnector, type StateHandleRecord, type StateHandleStore, type StateInput, type StateMutationInput, type StatePatchInput, stateHandleRecordForMutation, toPublicStateHandleRecord, } from './state-handles.js';
22
23
  export type { ToolDispatchContext, ToolDispatchDecision, ToolDispatchHook, } from './tool-dispatch.js';
23
24
  //# sourceMappingURL=index.d.ts.map
@@ -15,5 +15,6 @@ export { resolveManagedOrigins, } from './managed-origins.js';
15
15
  export { AllowAllPolicy } from './policy/allow-all.js';
16
16
  export { isConfirmationRequired, isInputRequired, isInputRequiredForConfirmation, } from './result.js';
17
17
  export { splitResultMeta } from './result-meta.js';
18
+ export { claimableStateHandleNames, } from './state-handle-ownership.js';
18
19
  export { assertExpectedStateRevision, assertNoSecretValue, assertSchemaValue, assertStateHandleNotCompleted, COMPLETE_STATE_OPERATION, createMutableStateHandleRecord, createStateConnector, InMemoryStateHandleStore, PATCH_STATE_OPERATION, READ_STATE_OPERATION, STATE_CONNECTOR_ID, STATE_CONNECTOR_VERSION, STATE_OPERATION_SIGNATURES, StateConnector, stateHandleRecordForMutation, toPublicStateHandleRecord, } from './state-handles.js';
19
20
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,21 @@
1
+ import type { ArtifactState } from '@noodle-borg/compiler';
2
+ export interface CallerStateAdoptionInput {
3
+ readonly deploymentId: string;
4
+ readonly handles: readonly string[];
5
+ readonly sourceCallerSubject: string;
6
+ readonly targetCallerSubject: string;
7
+ /** The old anonymous session's absolute expiry; redirects never extend its authority. */
8
+ readonly redirectExpiresAt: Date;
9
+ readonly now: Date;
10
+ }
11
+ export type CallerStateAdoptionResult = {
12
+ readonly ok: true;
13
+ readonly adoptedHandles: readonly string[];
14
+ readonly adoptedRecords: number;
15
+ } | {
16
+ readonly ok: false;
17
+ readonly reason: 'state_key_conflict';
18
+ };
19
+ /** The canonical sorted declaration projection stored on an elevation ticket. */
20
+ export declare function claimableStateHandleNames(state: ArtifactState | undefined): readonly string[];
21
+ //# sourceMappingURL=state-handle-ownership.d.ts.map
@@ -0,0 +1,10 @@
1
+ /** The canonical sorted declaration projection stored on an elevation ticket. */
2
+ export function claimableStateHandleNames(state) {
3
+ if (state === undefined)
4
+ return [];
5
+ return Object.entries(state.handles)
6
+ .filter(([, handle]) => handle.claimOnAuthentication === true)
7
+ .map(([name]) => name)
8
+ .sort();
9
+ }
10
+ //# sourceMappingURL=state-handle-ownership.js.map
@@ -12,6 +12,10 @@
12
12
  ".": {
13
13
  "types": "./dist/index.d.ts",
14
14
  "import": "./dist/index.js"
15
+ },
16
+ "./postgres": {
17
+ "types": "./dist/postgres.d.ts",
18
+ "import": "./dist/postgres.js"
15
19
  }
16
20
  },
17
21
  "files": [
@@ -26,6 +30,7 @@
26
30
  "@noodle-borg/module": "0.0.0"
27
31
  },
28
32
  "devDependencies": {
29
- "@types/node": "^24.13.1"
33
+ "@types/node": "^24.13.1",
34
+ "@types/pg": "^8.20.0"
30
35
  }
31
36
  }
@@ -1,6 +1,6 @@
1
1
  import { ADMISSION_DEFAULTS } from '@noodle-borg/admission-limits/portable';
2
2
  import { assistantCoreModelMessages, assistantTurnModelContextMessages, requestAssistantSuggestedPrompts, requestModelCompletion, resolveAssistantContextProviderModelResult, } from '@noodle-borg/assistant-gateway/model-runtime';
3
- import { assistantGuideModelContext, assistantModelToolOncePerSession, assistantModelToolRequiredWhenVisible, assistantOmittedToolResult, assistantViewAvailableData, dispatchAssistantTool, projectAssistantGuide, publicSurfaceOf, selectAssistantModelTools, toolTouchesDelegatedAuth, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
3
+ import { assistantGuideModelContext, assistantModelToolOncePerSession, assistantModelToolRequiredWhenVisible, assistantOmittedToolResult, assistantViewAvailableData, dispatchAssistantTool, projectAssistantGuide, publicSurfaceOf, recoverableAssistantView, remainingTurnTokens, selectAssistantModelTools, toolTouchesDelegatedAuth, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
4
4
  import { validateJsonSchemaWithDefaults } from '@noodle-borg/compiler';
5
5
  import { guardedFetch } from '@noodle-borg/connector-http';
6
6
  import { evaluateToolAuthorization } from '@noodle-borg/protocol';
@@ -122,7 +122,7 @@ export async function runAgentTurn(target, session, message, context, deps, emit
122
122
  stats.reasoningTokens += completion.usage?.reasoningTokens ?? 0;
123
123
  stats.totalTokens += completion.usage?.totalTokens ?? 0;
124
124
  if (remainingTokens !== undefined) {
125
- remainingTokens = Math.max(0, remainingTokens - (completion.usage?.completionTokens ?? requestTokenLimit));
125
+ remainingTokens = remainingTurnTokens(remainingTokens, completion.usage, requestTokenLimit);
126
126
  }
127
127
  const response = completion.choices[0]?.message;
128
128
  if (!response)
@@ -207,6 +207,9 @@ export async function runAgentTurn(target, session, message, context, deps, emit
207
207
  tool,
208
208
  session,
209
209
  assistant,
210
+ ...(target.served.artifact.server.state === undefined
211
+ ? {}
212
+ : { state: target.served.artifact.server.state }),
210
213
  elevations: deps.elevations,
211
214
  ...(delegatedKeys !== undefined && delegatedKeys.size > 0
212
215
  ? {
@@ -269,8 +272,10 @@ export async function runAgentTurn(target, session, message, context, deps, emit
269
272
  result: dispatch.output,
270
273
  arguments: args,
271
274
  }, (failure) => deps.logger?.warn('assistant.view.unresolved', { ...failure }));
272
- if (view)
275
+ if (view) {
276
+ await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
273
277
  emit({ event: 'view_available', data: { ...view } });
278
+ }
274
279
  messages.push({
275
280
  role: 'tool',
276
281
  tool_call_id: call.id,
@@ -1,5 +1,6 @@
1
1
  import { completeElevation, publicSurfaceOf } from '@noodle-borg/assistant-gateway/portable';
2
2
  import { anonymousBehavior } from '@noodle-borg/compiler';
3
+ import { claimableStateHandleNames } from '@noodle-borg/runtime';
3
4
  import { assistantSessionResponseSchema } from '@noodle-borg/wire-contracts';
4
5
  import { sendJson } from '../http-util.js';
5
6
  function isMixed(surface) {
@@ -16,6 +17,7 @@ const ELEVATION_REFUSAL_MESSAGES = {
16
17
  elevation_tenant_mismatch: 'sign-in ticket belongs to a different tenant',
17
18
  elevation_session_unavailable: 'the conversation behind this sign-in is no longer available',
18
19
  elevation_already_signed_in: 'this conversation is already signed in',
20
+ elevation_state_conflict: 'the signed-in account already has state at the same key; restart or resolve that draft first',
19
21
  };
20
22
  /**
21
23
  * Decide whether this call is an elevation instead of an execution.
@@ -40,6 +42,7 @@ export async function interceptForElevation(input) {
40
42
  sessionId: input.session.id,
41
43
  tenant: input.session.tenant,
42
44
  tool: input.tool.name,
45
+ claimableStateHandles: claimableStateHandleNames(input.state),
43
46
  now: input.now,
44
47
  });
45
48
  return {
@@ -84,7 +87,7 @@ export async function elevateAssistantSession(res, deps, input) {
84
87
  code: 'elevation_unavailable',
85
88
  });
86
89
  }
87
- const elevated = await completeElevation({
90
+ const request = {
88
91
  continuation: input.signInTicket,
89
92
  tenant: input.client.tenant,
90
93
  caller: input.caller,
@@ -95,11 +98,14 @@ export async function elevateAssistantSession(res, deps, input) {
95
98
  ...(input.customerRouting ? { customerRouting: input.customerRouting } : {}),
96
99
  ...(input.boundSurface ? { boundSurface: input.boundSurface } : {}),
97
100
  resume: input.resume,
98
- }, {
99
- elevations: deps.elevations,
100
- elevateSession: (request) => deps.store.elevateSession(request),
101
- now: () => deps.clock?.() ?? new Date(),
102
- });
101
+ };
102
+ const elevated = deps.elevationCoordinator
103
+ ? await deps.elevationCoordinator.complete(request)
104
+ : await completeElevation(request, {
105
+ elevations: deps.elevations,
106
+ elevateSession: (elevation) => deps.store.elevateSession(elevation),
107
+ now: () => deps.clock?.() ?? new Date(),
108
+ });
103
109
  if (!elevated.ok) {
104
110
  await deps.audit.emit({
105
111
  eventType: 'assistant.session.elevation_refused',
@@ -137,7 +143,8 @@ export async function elevateAssistantSession(res, deps, input) {
137
143
  endpoints: input.endpoints,
138
144
  ...(input.configuration ? { configuration: input.configuration } : {}),
139
145
  // The armed hint: the widget answers with one { resume: true } turn on the turns endpoint.
140
- ...(input.resume ? { resume: { tool: elevated.tool } } : {}),
146
+ ...(elevated.resumeArmed ? { resume: { tool: elevated.tool } } : {}),
147
+ continuedAfterAuthentication: true,
141
148
  };
142
149
  assistantSessionResponseSchema.parse(body);
143
150
  return sendJson(res, 200, body);
@@ -1,4 +1,4 @@
1
- import { assistantInteractionTool, assistantViewAvailableData, auditAssistantInteraction, } from '@noodle-borg/assistant-gateway/portable';
1
+ import { assistantInteractionTool, assistantViewAvailableData, auditAssistantInteraction, recoverableAssistantView, } from '@noodle-borg/assistant-gateway/portable';
2
2
  import { sendJson } from '../http-util.js';
3
3
  import { now } from './assistant.js';
4
4
  import { assistantInteractionScope } from './assistant-interaction-resolution.js';
@@ -71,8 +71,10 @@ export async function replayOrConflict(res, deps, session, interaction, action)
71
71
  replayed: true,
72
72
  }, (failure) => deps.logger?.warn('assistant.view.unresolved', { ...failure }))
73
73
  : undefined;
74
- if (view)
74
+ if (view) {
75
+ await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
75
76
  writeViewAvailable(res, view);
77
+ }
76
78
  }
77
79
  else if (current.status === 'failed') {
78
80
  writeInteractionError(res, current.publicOutcome.code, false);
@@ -1,4 +1,4 @@
1
- import { AssistantInteractionCapacityError, assistantConfirmationReview, assistantConfirmationTransition, assistantInputTransition, assistantInteractionTool, assistantPreparedArgumentReview, assistantPreparedToolContinuation, assistantSafeOutput, assistantToolContinuation, assistantViewAvailableData, auditAssistantInteraction, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
1
+ import { AssistantInteractionCapacityError, assistantConfirmationReview, assistantConfirmationTransition, assistantInputTransition, assistantInteractionTool, assistantPreparedArgumentReview, assistantPreparedToolContinuation, assistantSafeOutput, assistantToolContinuation, assistantViewAvailableData, auditAssistantInteraction, recoverableAssistantView, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
2
2
  import { executePreparedTool, executeToolInteractive, resumeTool, resumeToolPreparation, } from '@noodle-borg/runtime';
3
3
  import { readJsonBody, sendJson } from '../http-util.js';
4
4
  import { applyBrowserCors, authenticateSession, now, } from './assistant.js';
@@ -364,8 +364,10 @@ async function finishExecution(res, deps, session, interaction, context, result,
364
364
  tool: toolName,
365
365
  result: safeOutput,
366
366
  }, (failure) => deps.logger?.warn('assistant.view.unresolved', { ...failure }));
367
- if (view)
367
+ if (view) {
368
+ await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
368
369
  writeViewAvailable(res, view);
370
+ }
369
371
  await narrateResolvedInteraction(res, deps, target, session, toolName, 'accept', safeOutput, context, suggestions);
370
372
  return endInteractionSse(res);
371
373
  }
@@ -1,7 +1,7 @@
1
- import { visibleTranscript } from '@noodle-borg/assistant-gateway/portable';
2
- import { assistantTranscriptResponseSchema } from '@noodle-borg/wire-contracts';
3
- import { sendJson } from '../http-util.js';
1
+ import { assistantConfirmationProposal, assistantTranscriptEvents, assistantViewAvailableData, visibleTranscript, } from '@noodle-borg/assistant-gateway/portable';
2
+ import { assistantWireEventSchema } from '@noodle-borg/wire-contracts';
4
3
  import { applyBrowserCors, authenticateSession } from './assistant-route-http.js';
4
+ import { sessionScopedTarget } from './assistant-session-target.js';
5
5
  /**
6
6
  * The bounded visible-transcript read (ADR 0141/0201 amendments 2026-08-26), in its own module for
7
7
  * the same reason `assistant-elevation.ts` is: `assistant.ts` is at its size limit, and this is a
@@ -18,15 +18,54 @@ export async function handleAssistantTranscript(req, res, deps) {
18
18
  if (!session)
19
19
  return;
20
20
  applyBrowserCors(req, res, session.origin);
21
- const body = {
21
+ const target = await sessionScopedTarget(deps.registry, session);
22
+ const latestView = target && session.latestView
23
+ ? assistantViewAvailableData(target.served.artifact, { ...session.latestView, replayed: true }, (failure) => deps.logger?.warn('assistant.view.unresolved', { ...failure }))
24
+ : undefined;
25
+ const pending = await deps.store.findPendingInteraction({
26
+ sessionId: session.id,
27
+ deploymentId: session.deploymentId,
28
+ now: deps.clock?.() ?? new Date(),
29
+ });
30
+ const pendingInteraction = target && pending && target.served.artifact.tools.some((tool) => tool.name === pending.tool)
31
+ ? pending.kind === 'confirmation'
32
+ ? {
33
+ event: 'tool_proposed',
34
+ data: {
35
+ id: pending.id,
36
+ tool: pending.tool,
37
+ ...assistantConfirmationProposal(pending.review),
38
+ expiresAt: pending.expiresAt,
39
+ requiresConfirmation: true,
40
+ },
41
+ }
42
+ : {
43
+ event: 'input_requested',
44
+ data: {
45
+ id: pending.id,
46
+ message: pending.message,
47
+ requestedSchema: pending.requestedSchema,
48
+ expiresAt: pending.expiresAt,
49
+ },
50
+ }
51
+ : undefined;
52
+ const events = assistantTranscriptEvents({
22
53
  entries: visibleTranscript(session.history),
23
54
  ...(session.latestSuggestions?.phase === 'follow_up'
24
55
  ? { suggestions: session.latestSuggestions }
25
56
  : {}),
26
- };
27
- // Parse before sending, like the session exchange: a contract break fails loudly here rather than
28
- // stranding deployed widgets (ADR 0151).
29
- assistantTranscriptResponseSchema.parse(body);
30
- return sendJson(res, 200, body);
57
+ ...(latestView === undefined ? {} : { latestView }),
58
+ ...(pendingInteraction === undefined ? {} : { pendingInteraction }),
59
+ });
60
+ res.writeHead(200, {
61
+ 'content-type': 'text/event-stream; charset=utf-8',
62
+ 'cache-control': 'no-store',
63
+ });
64
+ for (const event of events) {
65
+ // Parse before sending, like every assistant stream: contract drift fails at the service seam.
66
+ assistantWireEventSchema.parse(event);
67
+ res.write(`event: ${event.event}\ndata: ${JSON.stringify(event.data)}\n\n`);
68
+ }
69
+ res.end();
31
70
  }
32
71
  //# sourceMappingURL=assistant-transcript.js.map
@@ -241,6 +241,9 @@ export function createServiceHandler(registry, options = {}) {
241
241
  appearance: assistantAppearance,
242
242
  ...(options.publicEmbeds !== undefined ? { publicEmbeds: options.publicEmbeds } : {}),
243
243
  ...(options.elevations !== undefined ? { elevations: options.elevations } : {}),
244
+ ...(options.elevationCoordinator !== undefined
245
+ ? { elevationCoordinator: options.elevationCoordinator }
246
+ : {}),
244
247
  ...(options.admissionCounters !== undefined
245
248
  ? { admissionCounters: options.admissionCounters }
246
249
  : {}),
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@modelcontextprotocol/sdk": "^1.29.0",
41
41
  "@noodle-borg/admission-limits": "0.0.0",
42
- "@noodle-borg/agent-kit": "0.90.0",
42
+ "@noodle-borg/agent-kit": "0.91.0",
43
43
  "@noodle-borg/app-package": "0.0.0",
44
44
  "@noodle-borg/assistant-gateway": "0.0.0",
45
45
  "@noodle-borg/auth": "0.0.0",
@@ -30,28 +30,9 @@ export declare const assistantSessionResponseSchema: z.ZodObject<{
30
30
  resume: z.ZodOptional<z.ZodObject<{
31
31
  tool: z.ZodString;
32
32
  }, z.core.$strip>>;
33
+ continuedAfterAuthentication: z.ZodOptional<z.ZodLiteral<true>>;
33
34
  }, z.core.$strip>;
34
35
  export type AssistantSessionResponse = z.infer<typeof assistantSessionResponseSchema>;
35
- /**
36
- * The bounded visible transcript (ADR 0141/0201 amendments 2026-08-26): only rows that actually
37
- * appeared in the panel — never tool internals, tickets, or model-facing scaffolding. Bounded by the
38
- * session history window (40 messages); a read spends no turn budget. Strict, because the service
39
- * parses its own response before sending, like the session exchange.
40
- */
41
- export declare const assistantTranscriptResponseSchema: z.ZodObject<{
42
- entries: z.ZodArray<z.ZodObject<{
43
- role: z.ZodEnum<{
44
- assistant: "assistant";
45
- user: "user";
46
- }>;
47
- text: z.ZodString;
48
- }, z.core.$strict>>;
49
- suggestions: z.ZodOptional<z.ZodObject<{
50
- phase: z.ZodLiteral<"follow_up">;
51
- prompts: z.ZodArray<z.ZodString>;
52
- }, z.core.$strict>>;
53
- }, z.core.$strict>;
54
- export type AssistantTranscriptResponse = z.infer<typeof assistantTranscriptResponseSchema>;
55
36
  /** MCP-Apps-shaped, author-selected renderer summary included on one assistant turn. */
56
37
  export declare const assistantModelContextUpdateSchema: z.ZodObject<{
57
38
  content: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -150,6 +131,19 @@ export type AssistantInteractionRequest = z.infer<typeof assistantInteractionReq
150
131
  * proposal/completion payloads while clients talking to older services tolerate absent new fields.
151
132
  */
152
133
  export declare const assistantWireEventSchema: z.ZodUnion<readonly [z.ZodObject<{
134
+ event: z.ZodLiteral<string>;
135
+ data: z.ZodObject<{
136
+ message: z.ZodString;
137
+ }, z.core.$strip>;
138
+ }, z.core.$strip>, z.ZodObject<{
139
+ event: z.ZodLiteral<string>;
140
+ data: z.ZodObject<{
141
+ tool: z.ZodString;
142
+ }, z.core.$strip>;
143
+ }, z.core.$strip>, z.ZodObject<{
144
+ event: z.ZodLiteral<string>;
145
+ data: z.ZodObject<{}, z.core.$strip>;
146
+ }, z.core.$strip>, z.ZodObject<{
153
147
  event: z.ZodLiteral<string>;
154
148
  data: z.ZodObject<{
155
149
  turnId: z.ZodOptional<z.ZodString>;
@@ -210,18 +204,18 @@ export declare const assistantWireEventSchema: z.ZodUnion<readonly [z.ZodObject<
210
204
  }, z.core.$strip>, z.ZodObject<{
211
205
  event: z.ZodLiteral<string>;
212
206
  data: z.ZodObject<{
213
- turnId: z.ZodOptional<z.ZodString>;
214
- id: z.ZodString;
215
207
  tool: z.ZodString;
208
+ id: z.ZodString;
216
209
  resourceUri: z.ZodString;
217
210
  title: z.ZodOptional<z.ZodString>;
218
211
  result: z.ZodJSONSchema;
219
212
  arguments: z.ZodOptional<z.ZodJSONSchema>;
220
- html: z.ZodOptional<z.ZodString>;
221
213
  resourceMeta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
222
214
  allowedOpenDomains: z.ZodOptional<z.ZodArray<z.ZodURL>>;
215
+ turnId: z.ZodOptional<z.ZodString>;
216
+ html: z.ZodOptional<z.ZodString>;
223
217
  replayed: z.ZodOptional<z.ZodLiteral<true>>;
224
- }, z.core.$strip>;
218
+ }, z.core.$strict>;
225
219
  }, z.core.$strip>, z.ZodObject<{
226
220
  event: z.ZodLiteral<string>;
227
221
  data: z.ZodObject<{
@@ -237,8 +231,8 @@ export declare const assistantWireEventSchema: z.ZodUnion<readonly [z.ZodObject<
237
231
  data: z.ZodObject<{
238
232
  turnId: z.ZodOptional<z.ZodString>;
239
233
  phase: z.ZodEnum<{
240
- follow_up: "follow_up";
241
234
  initial: "initial";
235
+ follow_up: "follow_up";
242
236
  }>;
243
237
  prompts: z.ZodArray<z.ZodString>;
244
238
  }, z.core.$strip>;
@@ -44,9 +44,9 @@ export const assistantSessionResponseSchema = z.object({
44
44
  sandbox: z.url().optional(),
45
45
  /**
46
46
  * Additive bounded visible-transcript endpoint (ADR 0141/0201 amendments 2026-08-26): a
47
- * session-token-authenticated POST returning what the panel may repaint after a full-page
48
- * navigation — the sign-in redirect above all. Absent on older services; the widget then
49
- * starts visually fresh exactly as before.
47
+ * session-token-authenticated POST replaying standard assistant events after a full-page
48
+ * navigation — the sign-in redirect above all. Absent on older services; the widget then starts
49
+ * visually fresh exactly as before.
50
50
  */
51
51
  transcript: z.url().optional(),
52
52
  /** Additive model-generated prompt suggestions; its presence is the client capability gate. */
@@ -59,30 +59,21 @@ export const assistantSessionResponseSchema = z.object({
59
59
  * existing turns endpoint; older widgets strip unknown keys and simply never auto-resume.
60
60
  */
61
61
  resume: z.object({ tool: z.string().min(1) }).optional(),
62
+ /** Additive elevation marker; the client renders one bounded continuation status. */
63
+ continuedAfterAuthentication: z.literal(true).optional(),
62
64
  });
63
- /**
64
- * The bounded visible transcript (ADR 0141/0201 amendments 2026-08-26): only rows that actually
65
- * appeared in the panel — never tool internals, tickets, or model-facing scaffolding. Bounded by the
66
- * session history window (40 messages); a read spends no turn budget. Strict, because the service
67
- * parses its own response before sending, like the session exchange.
68
- */
69
- export const assistantTranscriptResponseSchema = z
65
+ const recoverableViewSchema = z
70
66
  .object({
71
- entries: z
72
- .array(z
73
- .object({
74
- role: z.enum(['user', 'assistant']),
75
- text: z.string(),
76
- })
77
- .strict())
78
- .max(40),
79
- suggestions: z
80
- .object({
81
- phase: z.literal('follow_up'),
82
- prompts: z.array(z.string().trim().min(1).max(240)).max(3),
83
- })
84
- .strict()
85
- .optional(),
67
+ id: z.string().min(1),
68
+ tool: z.string().min(1),
69
+ resourceUri: z.string().startsWith('ui://'),
70
+ title: z.string().min(1).optional(),
71
+ result: z.json(),
72
+ arguments: z.json().optional(),
73
+ html: z.string().min(1),
74
+ resourceMeta: z.record(z.string(), z.unknown()).optional(),
75
+ allowedOpenDomains: z.array(z.url().startsWith('https://')).optional(),
76
+ replayed: z.literal(true),
86
77
  })
87
78
  .strict();
88
79
  const assistantModelContentPartSchema = z
@@ -185,6 +176,9 @@ const eventEnvelope = (event, data) => z.object({ event: z.literal(event), data
185
176
  * proposal/completion payloads while clients talking to older services tolerate absent new fields.
186
177
  */
187
178
  export const assistantWireEventSchema = z.union([
179
+ eventEnvelope('message_started', z.object({ message: z.string().min(1) })),
180
+ eventEnvelope('resume_started', z.object({ tool: z.string() })),
181
+ eventEnvelope('message_completed', z.object({})),
188
182
  eventEnvelope('content', z.object({ turnId: optionalTurnId, delta: z.string() })),
189
183
  eventEnvelope('tool_started', z.object({
190
184
  turnId: optionalTurnId,
@@ -224,17 +218,9 @@ export const assistantWireEventSchema = z.union([
224
218
  resourceMeta: z.record(z.string(), z.unknown()).optional(),
225
219
  replayed: z.literal(true).optional(),
226
220
  })),
227
- eventEnvelope('view_available', z.object({
221
+ eventEnvelope('view_available', recoverableViewSchema.omit({ replayed: true }).extend({
228
222
  turnId: optionalTurnId,
229
- id: z.string().min(1),
230
- tool: z.string().min(1),
231
- resourceUri: z.string().startsWith('ui://'),
232
- title: z.string().min(1).optional(),
233
- result: z.json(),
234
- arguments: z.json().optional(),
235
223
  html: z.string().min(1).optional(),
236
- resourceMeta: z.record(z.string(), z.unknown()).optional(),
237
- allowedOpenDomains: z.array(z.url().startsWith('https://')).optional(),
238
224
  replayed: z.literal(true).optional(),
239
225
  })),
240
226
  eventEnvelope('auth_requested', z.object({
@@ -31,7 +31,7 @@ Runtime requirements: Node.js 20+ for the server helper; the package ships ESM a
31
31
  export conditions, so bundlers and plain Node resolve it without aliases or type shims.
32
32
 
33
33
  The full, always-current integration guide (access modes, session response contract, framework notes, and
34
- troubleshooting) lives at <https://docs.noodleseed.dev/guides/embedded-assistant>. Authoring and deploying
34
+ troubleshooting) lives at <https://docs.noodleseed.dev/docs/guides/embedded-assistant>. Authoring and deploying
35
35
  the server itself is `@noodleseed/one` (`npm install -g @noodleseed/one`).
36
36
 
37
37
  If your page sends a `Content-Security-Policy`, allow the Noodle service origin in `connect-src` (turns and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodleseed/one",
3
- "version": "0.151.0",
3
+ "version": "0.152.0",
4
4
  "private": false,
5
5
  "description": "Noodle CLI by Noodle Seed — author, run, and deploy declarative MCP servers. Embedding the assistant in your own web app is @noodleseed/assistant.",
6
6
  "license": "Apache-2.0",
@@ -236,7 +236,7 @@
236
236
  "@modelcontextprotocol/client": "2.0.0",
237
237
  "@modelcontextprotocol/server": "2.0.0",
238
238
  "@noodle-borg/admission-limits": "0.0.0",
239
- "@noodle-borg/agent-kit": "0.90.0",
239
+ "@noodle-borg/agent-kit": "0.91.0",
240
240
  "@noodle-borg/app-audit": "0.0.0",
241
241
  "@noodle-borg/app-package": "0.0.0",
242
242
  "@noodle-borg/assistant-gateway": "0.0.0",