@tangle-network/agent-app 0.43.41 → 0.43.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant/index.d.ts +2 -1
- package/dist/assistant/index.js +3 -2
- package/dist/assistant/index.js.map +1 -1
- package/dist/chat-routes/index.d.ts +17 -3
- package/dist/chat-routes/index.js +91 -6
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +4 -3
- package/dist/chat-store/index.js +5 -1
- package/dist/chat-store/index.js.map +1 -1
- package/dist/{chunk-Y4QHNQ75.js → chunk-6KVH5SP5.js} +110 -6
- package/dist/chunk-6KVH5SP5.js.map +1 -0
- package/dist/{chunk-XYWCGFII.js → chunk-B5JD3DXD.js} +48 -2
- package/dist/chunk-B5JD3DXD.js.map +1 -0
- package/dist/{chunk-I2R2XT4M.js → chunk-I2ATYB7R.js} +18 -2
- package/dist/chunk-I2ATYB7R.js.map +1 -0
- package/dist/{chunk-KM766NN3.js → chunk-PEUBCJXF.js} +1772 -1224
- package/dist/chunk-PEUBCJXF.js.map +1 -0
- package/dist/{chunk-UMSOSUEU.js → chunk-QYOD3K56.js} +2 -2
- package/dist/chunk-QYOD3K56.js.map +1 -0
- package/dist/chunk-SIXYZ2FB.js +101 -0
- package/dist/chunk-SIXYZ2FB.js.map +1 -0
- package/dist/{chunk-4TXDD6P2.js → chunk-XAWFPMAR.js} +38 -3
- package/dist/chunk-XAWFPMAR.js.map +1 -0
- package/dist/chunk-ZU5GNSOJ.js +920 -0
- package/dist/chunk-ZU5GNSOJ.js.map +1 -0
- package/dist/{contract-DYbTzEDf.d.ts → contract-KfqJh_au.d.ts} +23 -2
- package/dist/design-canvas-react/index.js +4 -4
- package/dist/durable-chat/index.d.ts +419 -0
- package/dist/durable-chat/index.js +59 -0
- package/dist/durable-chat/index.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +124 -46
- package/dist/interactions/index.d.ts +58 -3
- package/dist/interactions/index.js +7 -3
- package/dist/object-store/index.d.ts +6 -4
- package/dist/object-store/index.js +1 -1
- package/dist/{parts-BcbitSNp.d.ts → parts-DjX0RRTS.d.ts} +9 -3
- package/dist/plans/index.d.ts +69 -0
- package/dist/plans/index.js +21 -0
- package/dist/plans/index.js.map +1 -0
- package/dist/stream/index.js +3 -1
- package/dist/teams/index.js +9 -9
- package/dist/teams/invitations-api.js +3 -3
- package/dist/web-react/index.d.ts +252 -93
- package/dist/web-react/index.js +32 -3
- package/package.json +11 -1
- package/dist/chunk-4TXDD6P2.js.map +0 -1
- package/dist/chunk-I2R2XT4M.js.map +0 -1
- package/dist/chunk-KM766NN3.js.map +0 -1
- package/dist/chunk-UMSOSUEU.js.map +0 -1
- package/dist/chunk-XYWCGFII.js.map +0 -1
- package/dist/chunk-Y4QHNQ75.js.map +0 -1
|
@@ -2,6 +2,9 @@ import {
|
|
|
2
2
|
SeatLimitError
|
|
3
3
|
} from "../chunk-SWUVTGMR.js";
|
|
4
4
|
import "../chunk-3SVAA3MA.js";
|
|
5
|
+
import {
|
|
6
|
+
hasWorkspaceRole
|
|
7
|
+
} from "../chunk-63CE7FEZ.js";
|
|
5
8
|
import {
|
|
6
9
|
generateInvitationToken,
|
|
7
10
|
getInvitationExpiresAt,
|
|
@@ -9,9 +12,6 @@ import {
|
|
|
9
12
|
normalizeInvitationEmail,
|
|
10
13
|
parseInvitationPermission
|
|
11
14
|
} from "../chunk-WEBBJBDH.js";
|
|
12
|
-
import {
|
|
13
|
-
hasWorkspaceRole
|
|
14
|
-
} from "../chunk-63CE7FEZ.js";
|
|
15
15
|
|
|
16
16
|
// src/teams/invitations-api.ts
|
|
17
17
|
import { and, eq, lte, sql } from "drizzle-orm";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export { d as ComposerAnswerDelivery, I as INTERACTION_CANCEL_EVENT, e as INTERACTION_EVENT, f as INTERACTION_RESOLVED_EVENT,
|
|
3
|
+
import { a as ChatInteractionField, h as InteractionAnswers, b as ChatInteractionStatus, C as ChatInteraction, i as InteractionCancelData, c as ChatSelectField, k as InteractionRequestWire } from '../contract-KfqJh_au.js';
|
|
4
|
+
export { d as ComposerAnswerDelivery, I as INTERACTION_CANCEL_EVENT, e as INTERACTION_EVENT, f as INTERACTION_RESOLVED_EVENT, g as InteractionAnswerValue, j as InteractionPersistedPart, N as NoticeKind, l as NoticePersistedPart, P as ParseInteractionAnswersResult, m as ParseInteractionResult, n as canTransitionInteractionStatus, o as cancelStatusFor, p as composerAnswerData, q as composerAnswerDeliveries, r as dedupeQuestionInteractionsByContent, s as fieldAcceptsFreeText, t as interactionFromWireRequest, u as interactionPartKey, v as interactionToPersistedPart, w as isRenderableInteractionKind, x as isSafeInteractionFieldKey, y as isTerminalInteractionStatus, z as noticePart, A as noticePartKey, B as parseInteractionAnswers, D as parseInteractionCancel, E as parseInteractionRequest, F as persistedPartToInteraction, G as questionInteractionContentSignature, H as stampInteractionAnswers } from '../contract-KfqJh_au.js';
|
|
5
|
+
import { ChatPlan } from '../plans/index.js';
|
|
5
6
|
import { InteractionData } from '@tangle-network/agent-interface';
|
|
6
7
|
export { InteractionData, InteractionOutcome, InteractionRequest } from '@tangle-network/agent-interface';
|
|
7
8
|
import { j as FileMention } from '../file-index-Bw_IQE_G.js';
|
|
@@ -12,6 +13,189 @@ export { S as SandboxTerminalConnection, a as SandboxTerminalConnectionResponse,
|
|
|
12
13
|
import { CatalogModel } from '../catalog/index.js';
|
|
13
14
|
import { Harness } from '../harness/index.js';
|
|
14
15
|
|
|
16
|
+
type DurablePlanDecision = 'approved' | 'rejected';
|
|
17
|
+
/** Stable authority receipt for the follow-up turn dispatched by a plan
|
|
18
|
+
* decision. Consumers must make `attachFollowUp` idempotent by `receiptId`;
|
|
19
|
+
* reload and retry deliberately invoke it again. */
|
|
20
|
+
interface DurablePlanFollowUpReceipt {
|
|
21
|
+
receiptId: string;
|
|
22
|
+
planId: string;
|
|
23
|
+
revision: number;
|
|
24
|
+
turnId: string;
|
|
25
|
+
state: string;
|
|
26
|
+
}
|
|
27
|
+
interface DurablePlanDecisionResult {
|
|
28
|
+
plan: ChatPlan;
|
|
29
|
+
followUp?: DurablePlanFollowUpReceipt;
|
|
30
|
+
idempotent: boolean;
|
|
31
|
+
projectionPending?: boolean;
|
|
32
|
+
effectPending?: boolean;
|
|
33
|
+
}
|
|
34
|
+
interface DurablePlanDecisionInput {
|
|
35
|
+
planId: string;
|
|
36
|
+
revision: number;
|
|
37
|
+
decision: DurablePlanDecision;
|
|
38
|
+
feedback?: string;
|
|
39
|
+
}
|
|
40
|
+
interface DurablePlanCurrentInput {
|
|
41
|
+
planId: string;
|
|
42
|
+
revision?: number;
|
|
43
|
+
}
|
|
44
|
+
interface DurablePlanDecisionClient {
|
|
45
|
+
current: (input: DurablePlanCurrentInput) => Promise<DurablePlanDecisionResult>;
|
|
46
|
+
decide: (input: DurablePlanDecisionInput) => Promise<DurablePlanDecisionResult>;
|
|
47
|
+
}
|
|
48
|
+
declare class DurablePlanClientError extends Error {
|
|
49
|
+
readonly status: number;
|
|
50
|
+
readonly code?: string | undefined;
|
|
51
|
+
readonly currentPlan?: ChatPlan | undefined;
|
|
52
|
+
constructor(message: string, status: number, code?: string | undefined, currentPlan?: ChatPlan | undefined);
|
|
53
|
+
}
|
|
54
|
+
interface DurablePlanDecisionClientOptions {
|
|
55
|
+
url: string | ((input: DurablePlanCurrentInput | DurablePlanDecisionInput) => string);
|
|
56
|
+
body?: Record<string, unknown> | ((input: DurablePlanDecisionInput) => Record<string, unknown>);
|
|
57
|
+
fetchImpl?: typeof fetch;
|
|
58
|
+
}
|
|
59
|
+
/** Browser client for the shared durable-plan route. The route URL and all
|
|
60
|
+
* product routing fields are injected; workspace/session identity is still
|
|
61
|
+
* resolved and authorized on the server. */
|
|
62
|
+
declare function createDurablePlanDecisionClient(options: DurablePlanDecisionClientOptions): DurablePlanDecisionClient;
|
|
63
|
+
interface UseDurablePlanFlowOptions {
|
|
64
|
+
plan: ChatPlan;
|
|
65
|
+
client: DurablePlanDecisionClient;
|
|
66
|
+
/** Must be idempotent by receipt.receiptId. */
|
|
67
|
+
attachFollowUp?: (receipt: DurablePlanFollowUpReceipt) => Promise<void> | void;
|
|
68
|
+
onUpdated?: (plan: ChatPlan) => void;
|
|
69
|
+
}
|
|
70
|
+
interface UseDurablePlanFlowResult {
|
|
71
|
+
plan: ChatPlan;
|
|
72
|
+
deciding: DurablePlanDecision | null;
|
|
73
|
+
restoring: boolean;
|
|
74
|
+
error: string | null;
|
|
75
|
+
decide: (decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>;
|
|
76
|
+
restore: () => Promise<DurablePlanDecisionResult | null>;
|
|
77
|
+
clearError: () => void;
|
|
78
|
+
}
|
|
79
|
+
/** Shared plan decision controller. It coalesces only concurrent attachment
|
|
80
|
+
* attempts; a later retry/restore calls the consumer's idempotent transport
|
|
81
|
+
* again so a lost response cannot strand an already-dispatched follow-up. */
|
|
82
|
+
declare function useDurablePlanFlow(options: UseDurablePlanFlowOptions): UseDurablePlanFlowResult;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Shared answer-building + submit plumbing for the interaction cards
|
|
86
|
+
* (question, plan). Client-safe, no React: cards own their state, this owns
|
|
87
|
+
* the wire. Lifted from the gtm-agent fork (the most fix-absorbed of the three
|
|
88
|
+
* product copies), including the 30s submit timeout that keeps a dead route
|
|
89
|
+
* from wedging a card in "Submitting…".
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
/** Status-badge labels for an interaction card. `cancelled`/`expired` read the
|
|
93
|
+
* same across cards; each card supplies its own verbs for the other states
|
|
94
|
+
* (a question is answered/declined; a plan is approved/rejected). */
|
|
95
|
+
declare function interactionStatusLabels(labels: {
|
|
96
|
+
pending: string;
|
|
97
|
+
answered: string;
|
|
98
|
+
declined: string;
|
|
99
|
+
}): Record<ChatInteractionStatus, string>;
|
|
100
|
+
/** Terminal-state notes for an interaction card. The expiry/withdrawal lines
|
|
101
|
+
* share one shape around the card's noun ("question"/"plan"); any extra notes
|
|
102
|
+
* (e.g. a plan's `declined` revision line) merge on top. */
|
|
103
|
+
declare function interactionTerminalNotes(noun: string, extra?: Partial<Record<ChatInteractionStatus, string>>): Partial<Record<ChatInteractionStatus, string>>;
|
|
104
|
+
type FieldValues = Record<string, {
|
|
105
|
+
selected?: string[];
|
|
106
|
+
text?: string;
|
|
107
|
+
custom?: string;
|
|
108
|
+
}>;
|
|
109
|
+
/** Converts acknowledged, persisted answers back into the local field state
|
|
110
|
+
* consumed by the shared cards. Persisted values are authoritative: this is
|
|
111
|
+
* intentionally used only when an interaction carries `answers`, never to
|
|
112
|
+
* guess an answer from the absence of an outstanding sidecar ask. */
|
|
113
|
+
declare function fieldValuesFromAnswers(fields: ChatInteractionField[], answers: InteractionAnswers | undefined): FieldValues;
|
|
114
|
+
/** The submitted value for one field, or null when it has no answer yet. */
|
|
115
|
+
declare function fieldAnswer(field: ChatInteractionField, values: FieldValues): InteractionData[string] | null;
|
|
116
|
+
/** All required fields answered → the respond payload; else null (not
|
|
117
|
+
* submittable yet). Optional unanswered fields are omitted. */
|
|
118
|
+
declare function buildAnswerData(fields: ChatInteractionField[], values: FieldValues): InteractionData | null;
|
|
119
|
+
declare function isLateAnswerableStatus(status: ChatInteractionStatus): boolean;
|
|
120
|
+
/** Secrets must never leave the sidecar answer channel for the visible chat
|
|
121
|
+
* transcript, so a secret-bearing ask cannot be late-answered. */
|
|
122
|
+
declare function hasSecretField(fields: ChatInteractionField[]): boolean;
|
|
123
|
+
/** Renders the late answer as a self-contained chat message: the original
|
|
124
|
+
* question, its context, and the user's answer(s). */
|
|
125
|
+
declare function lateAnswerMessage(interaction: ChatInteraction, data: InteractionData): string;
|
|
126
|
+
declare const INTERACTION_SUBMIT_TIMEOUT_MS = 30000;
|
|
127
|
+
declare const INTERACTION_SUBMIT_TIMEOUT_MESSAGE = "Could not reach the agent. Try again.";
|
|
128
|
+
/** One card submission: which ask, resolved how, with what answers. */
|
|
129
|
+
interface InteractionAnswerSubmission {
|
|
130
|
+
id: string;
|
|
131
|
+
outcome: 'accepted' | 'declined';
|
|
132
|
+
data?: InteractionData;
|
|
133
|
+
}
|
|
134
|
+
type InteractionSubmitResult = {
|
|
135
|
+
ok: true;
|
|
136
|
+
} | {
|
|
137
|
+
ok: false;
|
|
138
|
+
expired: boolean;
|
|
139
|
+
message: string;
|
|
140
|
+
};
|
|
141
|
+
/** The cards' only side-effect seam: POST one resolution, report the normalized
|
|
142
|
+
* outcome. Products bind their route URL + routing fields (workspaceId,
|
|
143
|
+
* threadId, session path param) via `createInteractionAnswerSubmitter` or a
|
|
144
|
+
* hand-rolled implementation. */
|
|
145
|
+
type SubmitInteractionAnswer = (submission: InteractionAnswerSubmission) => Promise<InteractionSubmitResult>;
|
|
146
|
+
/** Extracts the most specific error message a route returned. */
|
|
147
|
+
declare function responseErrorMessage(res: Response): Promise<{
|
|
148
|
+
code?: string;
|
|
149
|
+
message: string;
|
|
150
|
+
}>;
|
|
151
|
+
interface InteractionAnswerSubmitterOptions {
|
|
152
|
+
/** The product's answer route (the POST half of `createInteractionAnswerRoute`).
|
|
153
|
+
* A function when the URL carries the session (e.g. `/api/sessions/${id}/interactions`). */
|
|
154
|
+
url: string | ((submission: InteractionAnswerSubmission) => string);
|
|
155
|
+
/** Extra routing fields merged into the POST body (e.g. workspaceId, threadId). */
|
|
156
|
+
body?: Record<string, unknown> | ((submission: InteractionAnswerSubmission) => Record<string, unknown>);
|
|
157
|
+
timeoutMs?: number;
|
|
158
|
+
fetchImpl?: typeof fetch;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Builds the `SubmitInteractionAnswer` the cards consume: POSTs
|
|
162
|
+
* `{ ...routingFields, id, outcome, data? }` with an abortable timeout and
|
|
163
|
+
* normalizes the outcome. `expired` is the 410 path — the ask is gone
|
|
164
|
+
* (answered elsewhere, timed out, or the session moved on) and the card must
|
|
165
|
+
* flip to the same dead state a cancel event produces.
|
|
166
|
+
*/
|
|
167
|
+
declare function createInteractionAnswerSubmitter(options: InteractionAnswerSubmitterOptions): SubmitInteractionAnswer;
|
|
168
|
+
|
|
169
|
+
type DurableChatCard = {
|
|
170
|
+
kind: 'plan';
|
|
171
|
+
key: string;
|
|
172
|
+
plan: ChatPlan;
|
|
173
|
+
} | {
|
|
174
|
+
kind: 'interaction';
|
|
175
|
+
key: string;
|
|
176
|
+
interaction: ChatInteraction;
|
|
177
|
+
};
|
|
178
|
+
/** Converts persisted/live parts to canonical cards. Legacy interaction-plan
|
|
179
|
+
* cards are suppressed only when their raw part carries an explicit planId and
|
|
180
|
+
* revision matching a durable plan. Identical markdown alone is never proof. */
|
|
181
|
+
declare function durableChatCardsFromParts(parts: Array<Record<string, unknown>>): DurableChatCard[];
|
|
182
|
+
interface DurableChatCardsProps {
|
|
183
|
+
parts: Array<Record<string, unknown>>;
|
|
184
|
+
canWrite: boolean;
|
|
185
|
+
submitInteraction: SubmitInteractionAnswer;
|
|
186
|
+
decidePlan: (plan: ChatPlan, decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>;
|
|
187
|
+
decidingPlan?: (plan: ChatPlan) => DurablePlanDecision | null;
|
|
188
|
+
planError?: (plan: ChatPlan) => string | null;
|
|
189
|
+
onInteractionResolved?: (id: string, status: Exclude<ChatInteraction['status'], 'pending'>, answers?: InteractionAnswers) => void;
|
|
190
|
+
onLateAnswer?: (message: string) => boolean | void | Promise<boolean | void>;
|
|
191
|
+
renderMarkdown?: (markdown: string) => ReactNode;
|
|
192
|
+
className?: string;
|
|
193
|
+
}
|
|
194
|
+
/** Ready-to-embed canonical question/plan card lane for persisted assistant
|
|
195
|
+
* parts. Apps inject transport and styling callbacks instead of rebuilding the
|
|
196
|
+
* lifecycle/render switch. */
|
|
197
|
+
declare function DurableChatCards({ parts, canWrite, submitInteraction, decidePlan, decidingPlan, planError, onInteractionResolved, onLateAnswer, renderMarkdown, className, }: DurableChatCardsProps): react.JSX.Element | null;
|
|
198
|
+
|
|
15
199
|
/**
|
|
16
200
|
* Client-side chat-stream consumption — the NDJSON parse loop every agent
|
|
17
201
|
* app's chat UI hand-rolls (and breaks). Normalizes the three line shapes the
|
|
@@ -64,6 +248,10 @@ interface ChatStreamCallbacks {
|
|
|
64
248
|
* user", not "model working". Optional — a consumer that doesn't wire it
|
|
65
249
|
* parses the same stream unchanged. */
|
|
66
250
|
onInteraction?: (interaction: ChatInteraction) => void;
|
|
251
|
+
/** A terminal withdrawal/timeout for a previously emitted interaction. */
|
|
252
|
+
onInteractionCancel?: (cancel: InteractionCancelData) => void;
|
|
253
|
+
/** A durable-plan snapshot from any plan lifecycle event. */
|
|
254
|
+
onPlan?: (plan: ChatPlan) => void;
|
|
67
255
|
}
|
|
68
256
|
interface ConsumeChatStreamResult {
|
|
69
257
|
turnId: string | null;
|
|
@@ -166,86 +354,6 @@ interface ChatComposerProps {
|
|
|
166
354
|
}
|
|
167
355
|
declare function ChatComposer({ onSend, onSendParts, onCancel, isStreaming, disabled, placeholder, value, onValueChange, initialValue, seed, onSeedApplied, controls, controlsPlacement, onAttach, onAttachFolder, pendingFiles, onRemoveFile, accept, dropTitle, dropDescription, focusShortcut, sendLabel, className, }: ChatComposerProps): react.JSX.Element;
|
|
168
356
|
|
|
169
|
-
/**
|
|
170
|
-
* Shared answer-building + submit plumbing for the interaction cards
|
|
171
|
-
* (question, plan). Client-safe, no React: cards own their state, this owns
|
|
172
|
-
* the wire. Lifted from the gtm-agent fork (the most fix-absorbed of the three
|
|
173
|
-
* product copies), including the 30s submit timeout that keeps a dead route
|
|
174
|
-
* from wedging a card in "Submitting…".
|
|
175
|
-
*/
|
|
176
|
-
|
|
177
|
-
/** Status-badge labels for an interaction card. `cancelled`/`expired` read the
|
|
178
|
-
* same across cards; each card supplies its own verbs for the other states
|
|
179
|
-
* (a question is answered/declined; a plan is approved/rejected). */
|
|
180
|
-
declare function interactionStatusLabels(labels: {
|
|
181
|
-
pending: string;
|
|
182
|
-
answered: string;
|
|
183
|
-
declined: string;
|
|
184
|
-
}): Record<ChatInteractionStatus, string>;
|
|
185
|
-
/** Terminal-state notes for an interaction card. The expiry/withdrawal lines
|
|
186
|
-
* share one shape around the card's noun ("question"/"plan"); any extra notes
|
|
187
|
-
* (e.g. a plan's `declined` revision line) merge on top. */
|
|
188
|
-
declare function interactionTerminalNotes(noun: string, extra?: Partial<Record<ChatInteractionStatus, string>>): Partial<Record<ChatInteractionStatus, string>>;
|
|
189
|
-
type FieldValues = Record<string, {
|
|
190
|
-
selected?: string[];
|
|
191
|
-
text?: string;
|
|
192
|
-
custom?: string;
|
|
193
|
-
}>;
|
|
194
|
-
/** The submitted value for one field, or null when it has no answer yet. */
|
|
195
|
-
declare function fieldAnswer(field: ChatInteractionField, values: FieldValues): InteractionData[string] | null;
|
|
196
|
-
/** All required fields answered → the respond payload; else null (not
|
|
197
|
-
* submittable yet). Optional unanswered fields are omitted. */
|
|
198
|
-
declare function buildAnswerData(fields: ChatInteractionField[], values: FieldValues): InteractionData | null;
|
|
199
|
-
declare function isLateAnswerableStatus(status: ChatInteractionStatus): boolean;
|
|
200
|
-
/** Secrets must never leave the sidecar answer channel for the visible chat
|
|
201
|
-
* transcript, so a secret-bearing ask cannot be late-answered. */
|
|
202
|
-
declare function hasSecretField(fields: ChatInteractionField[]): boolean;
|
|
203
|
-
/** Renders the late answer as a self-contained chat message: the original
|
|
204
|
-
* question, its context, and the user's answer(s). */
|
|
205
|
-
declare function lateAnswerMessage(interaction: ChatInteraction, data: InteractionData): string;
|
|
206
|
-
declare const INTERACTION_SUBMIT_TIMEOUT_MS = 30000;
|
|
207
|
-
declare const INTERACTION_SUBMIT_TIMEOUT_MESSAGE = "Could not reach the agent. Try again.";
|
|
208
|
-
/** One card submission: which ask, resolved how, with what answers. */
|
|
209
|
-
interface InteractionAnswerSubmission {
|
|
210
|
-
id: string;
|
|
211
|
-
outcome: 'accepted' | 'declined';
|
|
212
|
-
data?: InteractionData;
|
|
213
|
-
}
|
|
214
|
-
type InteractionSubmitResult = {
|
|
215
|
-
ok: true;
|
|
216
|
-
} | {
|
|
217
|
-
ok: false;
|
|
218
|
-
expired: boolean;
|
|
219
|
-
message: string;
|
|
220
|
-
};
|
|
221
|
-
/** The cards' only side-effect seam: POST one resolution, report the normalized
|
|
222
|
-
* outcome. Products bind their route URL + routing fields (workspaceId,
|
|
223
|
-
* threadId, session path param) via `createInteractionAnswerSubmitter` or a
|
|
224
|
-
* hand-rolled implementation. */
|
|
225
|
-
type SubmitInteractionAnswer = (submission: InteractionAnswerSubmission) => Promise<InteractionSubmitResult>;
|
|
226
|
-
/** Extracts the most specific error message a route returned. */
|
|
227
|
-
declare function responseErrorMessage(res: Response): Promise<{
|
|
228
|
-
code?: string;
|
|
229
|
-
message: string;
|
|
230
|
-
}>;
|
|
231
|
-
interface InteractionAnswerSubmitterOptions {
|
|
232
|
-
/** The product's answer route (the POST half of `createInteractionAnswerRoute`).
|
|
233
|
-
* A function when the URL carries the session (e.g. `/api/sessions/${id}/interactions`). */
|
|
234
|
-
url: string | ((submission: InteractionAnswerSubmission) => string);
|
|
235
|
-
/** Extra routing fields merged into the POST body (e.g. workspaceId, threadId). */
|
|
236
|
-
body?: Record<string, unknown> | ((submission: InteractionAnswerSubmission) => Record<string, unknown>);
|
|
237
|
-
timeoutMs?: number;
|
|
238
|
-
fetchImpl?: typeof fetch;
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Builds the `SubmitInteractionAnswer` the cards consume: POSTs
|
|
242
|
-
* `{ ...routingFields, id, outcome, data? }` with an abortable timeout and
|
|
243
|
-
* normalizes the outcome. `expired` is the 410 path — the ask is gone
|
|
244
|
-
* (answered elsewhere, timed out, or the session moved on) and the card must
|
|
245
|
-
* flip to the same dead state a cancel event produces.
|
|
246
|
-
*/
|
|
247
|
-
declare function createInteractionAnswerSubmitter(options: InteractionAnswerSubmitterOptions): SubmitInteractionAnswer;
|
|
248
|
-
|
|
249
357
|
type InteractionBadgeVariant = 'outline' | 'default' | 'destructive';
|
|
250
358
|
declare function InteractionBadge({ variant, children }: {
|
|
251
359
|
variant: InteractionBadgeVariant;
|
|
@@ -282,7 +390,7 @@ interface InteractionQuestionCardProps {
|
|
|
282
390
|
submitAnswer: SubmitInteractionAnswer;
|
|
283
391
|
/** Fired when this card resolves locally (answered, or discovered expired
|
|
284
392
|
* via a 410) so the stream/route state stays in sync. */
|
|
285
|
-
onResolved?: (id: string, status: Exclude<ChatInteractionStatus, 'pending'
|
|
393
|
+
onResolved?: (id: string, status: Exclude<ChatInteractionStatus, 'pending'>, answers?: ChatInteraction['answers']) => void;
|
|
286
394
|
/** Delivers a late answer (the ask expired/was withdrawn) as a fresh chat
|
|
287
395
|
* turn. Return/resolve `false` when the send was rejected so the card stays
|
|
288
396
|
* retryable. Omit to hide the late-answer affordance entirely. */
|
|
@@ -307,6 +415,34 @@ interface InteractionPlanCardProps {
|
|
|
307
415
|
}
|
|
308
416
|
declare function InteractionPlanCard({ interaction, canWrite, submitAnswer, onResolved, renderMarkdown, className, }: InteractionPlanCardProps): react.JSX.Element;
|
|
309
417
|
|
|
418
|
+
interface DurablePlanCardProps {
|
|
419
|
+
plan: ChatPlan;
|
|
420
|
+
canWrite: boolean;
|
|
421
|
+
decide: (decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>;
|
|
422
|
+
deciding?: DurablePlanDecision | null;
|
|
423
|
+
error?: string | null;
|
|
424
|
+
renderMarkdown?: (markdown: string) => ReactNode;
|
|
425
|
+
className?: string;
|
|
426
|
+
}
|
|
427
|
+
declare function DurablePlanCard({ plan, canWrite, decide, deciding, error, renderMarkdown, className, }: DurablePlanCardProps): react.JSX.Element;
|
|
428
|
+
|
|
429
|
+
interface InteractionAttemptStore {
|
|
430
|
+
get(interactionId: string, submissionSignature: string): string | null;
|
|
431
|
+
set(interactionId: string, submissionSignature: string, attemptKey: string): void;
|
|
432
|
+
delete(interactionId: string, submissionSignature: string): void;
|
|
433
|
+
}
|
|
434
|
+
declare function createSessionInteractionAttemptStore(storage: Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>, namespace?: string): InteractionAttemptStore;
|
|
435
|
+
declare function createMemoryInteractionAttemptStore(): InteractionAttemptStore;
|
|
436
|
+
declare function interactionSubmissionSignature(submission: InteractionAnswerSubmission): string;
|
|
437
|
+
interface DurableInteractionAnswerSubmitterOptions extends InteractionAnswerSubmitterOptions {
|
|
438
|
+
attempts: InteractionAttemptStore;
|
|
439
|
+
createAttemptKey?: () => string;
|
|
440
|
+
}
|
|
441
|
+
/** Answer submitter for a durable interaction route. One opaque attempt key is
|
|
442
|
+
* retained for an ambiguous transport/5xx result and reused after reload. A
|
|
443
|
+
* changed answer has a different signature and therefore a new attempt. */
|
|
444
|
+
declare function createDurableInteractionAnswerSubmitter(options: DurableInteractionAnswerSubmitterOptions): SubmitInteractionAnswer;
|
|
445
|
+
|
|
310
446
|
/**
|
|
311
447
|
* useChatInteractions — the interaction-state wiring every consumer of
|
|
312
448
|
* `ChatStreamCallbacks.onInteraction` re-implements: an id-keyed,
|
|
@@ -336,14 +472,28 @@ declare function upsertChatInteraction(list: ChatInteraction[], interaction: Cha
|
|
|
336
472
|
* `expired` (reason:"timeout") or `cancelled`. */
|
|
337
473
|
declare function cancelChatInteraction(list: ChatInteraction[], cancel: InteractionCancelData): ChatInteraction[];
|
|
338
474
|
/** Marks one ask resolved locally (the card's `onResolved`). Forward-only. */
|
|
339
|
-
declare function resolveChatInteraction(list: ChatInteraction[], id: string, status: Exclude<ChatInteractionStatus, 'pending'
|
|
475
|
+
declare function resolveChatInteraction(list: ChatInteraction[], id: string, status: Exclude<ChatInteractionStatus, 'pending'>, answers?: InteractionAnswers): ChatInteraction[];
|
|
340
476
|
/** Settles every still-pending ask when the turn ends: `answered` for a turn
|
|
341
477
|
* that completed cleanly, `expired` for one that failed. */
|
|
342
478
|
declare function terminalizePendingChatInteractions(list: ChatInteraction[], status: Extract<ChatInteractionStatus, 'answered' | 'expired'>): ChatInteraction[];
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
479
|
+
type ChatInteractionRestoreMode = 'legacy' | 'durable';
|
|
480
|
+
interface RestoreChatInteractionsOptions {
|
|
481
|
+
/**
|
|
482
|
+
* `legacy` settles pending asks absent from the sidecar list as answered,
|
|
483
|
+
* preserving the pre-durable restore contract. `durable` leaves them
|
|
484
|
+
* pending because absence is ambiguous until `hydrateChatInteractions`
|
|
485
|
+
* applies the durable projection.
|
|
486
|
+
*/
|
|
487
|
+
mode?: ChatInteractionRestoreMode;
|
|
488
|
+
}
|
|
489
|
+
/** Reload restore from the answer route's GET list. Legacy consumers retain
|
|
490
|
+
* the historical absence→answered behavior; durable consumers opt into the
|
|
491
|
+
* ambiguity-preserving mode and apply terminal parts through `hydrate`. */
|
|
492
|
+
declare function restoreChatInteractions(list: ChatInteraction[], outstanding: InteractionRequestWire[], options?: RestoreChatInteractionsOptions): ChatInteraction[];
|
|
493
|
+
/** Applies transcript/state-store projections after reload. Terminal state and
|
|
494
|
+
* acknowledged answer values enrich an existing pending card without relying
|
|
495
|
+
* on the sidecar's outstanding-list absence. */
|
|
496
|
+
declare function hydrateChatInteractions(list: ChatInteraction[], persisted: ChatInteraction[]): ChatInteraction[];
|
|
347
497
|
interface UseChatInteractionsResult {
|
|
348
498
|
/** All known interactions, insertion-ordered. */
|
|
349
499
|
interactions: ChatInteraction[];
|
|
@@ -354,15 +504,18 @@ interface UseChatInteractionsResult {
|
|
|
354
504
|
/** Wire to `interaction.cancel` events. */
|
|
355
505
|
applyCancel: (cancel: InteractionCancelData) => void;
|
|
356
506
|
/** Wire to the cards' `onResolved`. */
|
|
357
|
-
markResolved: (id: string, status: Exclude<ChatInteractionStatus, 'pending'
|
|
507
|
+
markResolved: (id: string, status: Exclude<ChatInteractionStatus, 'pending'>, answers?: InteractionAnswers) => void;
|
|
358
508
|
/** Wire to the answer route's GET list after a reload/reconnect. */
|
|
359
|
-
restore: (outstanding: InteractionRequestWire[]) => void;
|
|
509
|
+
restore: (outstanding: InteractionRequestWire[], options?: RestoreChatInteractionsOptions) => void;
|
|
510
|
+
/** Apply durable transcript/state projections after a reload. */
|
|
511
|
+
hydrate: (persisted: ChatInteraction[]) => void;
|
|
360
512
|
/** Settle still-pending asks when the turn ends. */
|
|
361
513
|
terminalizePending: (status: Extract<ChatInteractionStatus, 'answered' | 'expired'>) => void;
|
|
362
514
|
/** Drop everything (thread switch). */
|
|
363
515
|
reset: () => void;
|
|
364
516
|
}
|
|
365
|
-
|
|
517
|
+
type UseChatInteractionsOptions = RestoreChatInteractionsOptions;
|
|
518
|
+
declare function useChatInteractions(options?: UseChatInteractionsOptions): UseChatInteractionsResult;
|
|
366
519
|
|
|
367
520
|
/**
|
|
368
521
|
* `useFileMentions` — the glue a host passes straight into `AgentComposer`'s
|
|
@@ -801,6 +954,9 @@ interface ChatUiMessage extends ChatMessageMetrics {
|
|
|
801
954
|
* present and non-empty it is rendered in place of `content` + `toolCalls`;
|
|
802
955
|
* both remain the fallback for producers that don't segment a turn. */
|
|
803
956
|
segments?: ChatMessageSegment[];
|
|
957
|
+
/** Persisted assistant parts. When `ChatMessages.durableCards` is supplied,
|
|
958
|
+
* shared plan/question cards render directly from these projections. */
|
|
959
|
+
parts?: Array<Record<string, unknown>>;
|
|
804
960
|
}
|
|
805
961
|
interface ChatMessagesProps {
|
|
806
962
|
messages: ChatUiMessage[];
|
|
@@ -810,6 +966,9 @@ interface ChatMessagesProps {
|
|
|
810
966
|
renderMarkdown?: (content: string) => ReactNode;
|
|
811
967
|
/** Extra per-message content (artifacts, custom panels) appended after the body. */
|
|
812
968
|
renderExtras?: (message: ChatUiMessage) => ReactNode;
|
|
969
|
+
/** Canonical durable plan/question card wiring. Apps inject only transport,
|
|
970
|
+
* access, and optional visual callbacks; card selection/dedupe stays shared. */
|
|
971
|
+
durableCards?: Omit<DurableChatCardsProps, 'parts' | 'renderMarkdown'>;
|
|
813
972
|
userLabel?: string;
|
|
814
973
|
agentLabel?: string;
|
|
815
974
|
/** Render the trailing "agent is thinking" row. */
|
|
@@ -882,6 +1041,6 @@ declare function useThinkingSeconds(active: boolean): number;
|
|
|
882
1041
|
* model id, tokens/sec, and cost, plus a collapsible thinking section and
|
|
883
1042
|
* tool-call chips.
|
|
884
1043
|
*/
|
|
885
|
-
declare function ChatMessages({ messages, models, renderMarkdown, renderExtras, userLabel, agentLabel, loading, approval, onToolCallClick, toolRenderers, error, onRetry, renderEmpty, emptyState, header, }: ChatMessagesProps): react.JSX.Element;
|
|
1044
|
+
declare function ChatMessages({ messages, models, renderMarkdown, renderExtras, durableCards, userLabel, agentLabel, loading, approval, onToolCallClick, toolRenderers, error, onRetry, renderEmpty, emptyState, header, }: ChatMessagesProps): react.JSX.Element;
|
|
886
1045
|
|
|
887
|
-
export { type ActivityTone, type AgentActivityPage, AgentActivityPanel, type AgentActivityPanelProps, type AgentActivityRecord, AgentSessionControls, type AgentSessionControlsProps, CatalogModel, ChatComposer, type ChatComposerProps, type ChatEmptyDoor, ChatEmptyState, type ChatEmptyStateProps, ChatInteraction, ChatInteractionField, ChatInteractionStatus, type ChatMessageMetrics, type ChatMessageSegment, ChatMessages, type ChatMessagesProps, ChatSelectField, type ChatStreamCallbacks, type ChatStreamToolCall, type ChatStreamToolResult, type ChatToolCallInfo, type ChatUiMessage, type ComposerFile, type ComposerFilePart, type ComposerMentionProp, type ConsumeChatStreamResult, DEFAULT_EFFORT_LEVELS, DEFAULT_MENTION_EMPTY_TEXT, DEFAULT_MENTION_LIMIT, type EffortLevel, EffortPicker, type EffortPickerProps, type FieldValues, FileMention, FlowWaterfall, type FlowWaterfallProps, INDEX_REFRESH_AFTER_MS, INTERACTION_SUBMIT_TIMEOUT_MESSAGE, INTERACTION_SUBMIT_TIMEOUT_MS, InteractionActionButton, type InteractionAnswerSubmission, type InteractionAnswerSubmitterOptions, InteractionBadge, type InteractionBadgeVariant, InteractionCancelData, InteractionPlanCard, type InteractionPlanCardProps, InteractionQuestionCard, type InteractionQuestionCardProps, InteractionRequestWire, type InteractionSubmitResult, type MentionItem, MissionActivityLane, type MissionActivityLaneProps, ModelPicker, type ModelPickerProps, type ProposalApprovalHandlers, ProviderLogo, type ProviderLogoProps, QuestionOptionList, type QuestionOptionListProps, RunDrillIn, type RunDrillInProps, SeatPaywall, type SeatPaywallProps, type SmoothRevealOptions, type StreamChatOptions, type SubmitInteractionAnswer, type ToolDetailRenderers, type ToolRunRecord, type ToolRunStep, type UseChatInteractionsResult, type UseFileMentionsOptions, type UseFileMentionsResult, type WaterfallRow, activityTone, buildAnswerData, cancelChatInteraction, consumeChatStream, createInteractionAnswerSubmitter, dispatchChatStreamLine, fieldAnswer, formatActivityCost, formatActivityDuration, formatModelCost, formatTokensPerSecond, hasSecretField, interactionStatusLabels, interactionTerminalNotes, isLateAnswerableStatus, lateAnswerMessage, mergeActivityPages, nextRevealCount, pendingApprovalOf, rankFileMentions, resolveChatInteraction, responseErrorMessage, restoreChatInteractions, streamChatTurn, terminalizePendingChatInteractions, upsertChatInteraction, useChatInteractions, useFileMentions, usePending, usePopover, useSmoothText, useThinkingSeconds, waterfallLayout };
|
|
1046
|
+
export { type ActivityTone, type AgentActivityPage, AgentActivityPanel, type AgentActivityPanelProps, type AgentActivityRecord, AgentSessionControls, type AgentSessionControlsProps, CatalogModel, ChatComposer, type ChatComposerProps, type ChatEmptyDoor, ChatEmptyState, type ChatEmptyStateProps, ChatInteraction, ChatInteractionField, type ChatInteractionRestoreMode, ChatInteractionStatus, type ChatMessageMetrics, type ChatMessageSegment, ChatMessages, type ChatMessagesProps, ChatSelectField, type ChatStreamCallbacks, type ChatStreamToolCall, type ChatStreamToolResult, type ChatToolCallInfo, type ChatUiMessage, type ComposerFile, type ComposerFilePart, type ComposerMentionProp, type ConsumeChatStreamResult, DEFAULT_EFFORT_LEVELS, DEFAULT_MENTION_EMPTY_TEXT, DEFAULT_MENTION_LIMIT, type DurableChatCard, DurableChatCards, type DurableChatCardsProps, type DurableInteractionAnswerSubmitterOptions, DurablePlanCard, type DurablePlanCardProps, DurablePlanClientError, type DurablePlanCurrentInput, type DurablePlanDecision, type DurablePlanDecisionClient, type DurablePlanDecisionClientOptions, type DurablePlanDecisionInput, type DurablePlanDecisionResult, type DurablePlanFollowUpReceipt, type EffortLevel, EffortPicker, type EffortPickerProps, type FieldValues, FileMention, FlowWaterfall, type FlowWaterfallProps, INDEX_REFRESH_AFTER_MS, INTERACTION_SUBMIT_TIMEOUT_MESSAGE, INTERACTION_SUBMIT_TIMEOUT_MS, InteractionActionButton, type InteractionAnswerSubmission, type InteractionAnswerSubmitterOptions, InteractionAnswers, type InteractionAttemptStore, InteractionBadge, type InteractionBadgeVariant, InteractionCancelData, InteractionPlanCard, type InteractionPlanCardProps, InteractionQuestionCard, type InteractionQuestionCardProps, InteractionRequestWire, type InteractionSubmitResult, type MentionItem, MissionActivityLane, type MissionActivityLaneProps, ModelPicker, type ModelPickerProps, type ProposalApprovalHandlers, ProviderLogo, type ProviderLogoProps, QuestionOptionList, type QuestionOptionListProps, type RestoreChatInteractionsOptions, RunDrillIn, type RunDrillInProps, SeatPaywall, type SeatPaywallProps, type SmoothRevealOptions, type StreamChatOptions, type SubmitInteractionAnswer, type ToolDetailRenderers, type ToolRunRecord, type ToolRunStep, type UseChatInteractionsOptions, type UseChatInteractionsResult, type UseDurablePlanFlowOptions, type UseDurablePlanFlowResult, type UseFileMentionsOptions, type UseFileMentionsResult, type WaterfallRow, activityTone, buildAnswerData, cancelChatInteraction, consumeChatStream, createDurableInteractionAnswerSubmitter, createDurablePlanDecisionClient, createInteractionAnswerSubmitter, createMemoryInteractionAttemptStore, createSessionInteractionAttemptStore, dispatchChatStreamLine, durableChatCardsFromParts, fieldAnswer, fieldValuesFromAnswers, formatActivityCost, formatActivityDuration, formatModelCost, formatTokensPerSecond, hasSecretField, hydrateChatInteractions, interactionStatusLabels, interactionSubmissionSignature, interactionTerminalNotes, isLateAnswerableStatus, lateAnswerMessage, mergeActivityPages, nextRevealCount, pendingApprovalOf, rankFileMentions, resolveChatInteraction, responseErrorMessage, restoreChatInteractions, streamChatTurn, terminalizePendingChatInteractions, upsertChatInteraction, useChatInteractions, useDurablePlanFlow, useFileMentions, usePending, usePopover, useSmoothText, useThinkingSeconds, waterfallLayout };
|
package/dist/web-react/index.js
CHANGED
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
DEFAULT_EFFORT_LEVELS,
|
|
8
8
|
DEFAULT_MENTION_EMPTY_TEXT,
|
|
9
9
|
DEFAULT_MENTION_LIMIT,
|
|
10
|
+
DurableChatCards,
|
|
11
|
+
DurablePlanCard,
|
|
12
|
+
DurablePlanClientError,
|
|
10
13
|
EffortPicker,
|
|
11
14
|
FlowWaterfall,
|
|
12
15
|
INDEX_REFRESH_AFTER_MS,
|
|
@@ -26,15 +29,23 @@ import {
|
|
|
26
29
|
buildAnswerData,
|
|
27
30
|
cancelChatInteraction,
|
|
28
31
|
consumeChatStream,
|
|
32
|
+
createDurableInteractionAnswerSubmitter,
|
|
33
|
+
createDurablePlanDecisionClient,
|
|
29
34
|
createInteractionAnswerSubmitter,
|
|
35
|
+
createMemoryInteractionAttemptStore,
|
|
36
|
+
createSessionInteractionAttemptStore,
|
|
30
37
|
dispatchChatStreamLine,
|
|
38
|
+
durableChatCardsFromParts,
|
|
31
39
|
fieldAnswer,
|
|
40
|
+
fieldValuesFromAnswers,
|
|
32
41
|
formatActivityCost,
|
|
33
42
|
formatActivityDuration,
|
|
34
43
|
formatModelCost,
|
|
35
44
|
formatTokensPerSecond,
|
|
36
45
|
hasSecretField,
|
|
46
|
+
hydrateChatInteractions,
|
|
37
47
|
interactionStatusLabels,
|
|
48
|
+
interactionSubmissionSignature,
|
|
38
49
|
interactionTerminalNotes,
|
|
39
50
|
isLateAnswerableStatus,
|
|
40
51
|
lateAnswerMessage,
|
|
@@ -49,13 +60,14 @@ import {
|
|
|
49
60
|
terminalizePendingChatInteractions,
|
|
50
61
|
upsertChatInteraction,
|
|
51
62
|
useChatInteractions,
|
|
63
|
+
useDurablePlanFlow,
|
|
52
64
|
useFileMentions,
|
|
53
65
|
usePending,
|
|
54
66
|
usePopover,
|
|
55
67
|
useSmoothText,
|
|
56
68
|
useThinkingSeconds,
|
|
57
69
|
waterfallLayout
|
|
58
|
-
} from "../chunk-
|
|
70
|
+
} from "../chunk-PEUBCJXF.js";
|
|
59
71
|
import {
|
|
60
72
|
tabTerminalConnectionId,
|
|
61
73
|
useSandboxTerminalConnection
|
|
@@ -85,11 +97,14 @@ import {
|
|
|
85
97
|
isTerminalInteractionStatus,
|
|
86
98
|
noticePart,
|
|
87
99
|
noticePartKey,
|
|
100
|
+
parseInteractionAnswers,
|
|
88
101
|
parseInteractionCancel,
|
|
89
102
|
parseInteractionRequest,
|
|
90
103
|
persistedPartToInteraction,
|
|
91
|
-
questionInteractionContentSignature
|
|
92
|
-
|
|
104
|
+
questionInteractionContentSignature,
|
|
105
|
+
stampInteractionAnswers
|
|
106
|
+
} from "../chunk-XAWFPMAR.js";
|
|
107
|
+
import "../chunk-SIXYZ2FB.js";
|
|
93
108
|
import "../chunk-E7QYOOON.js";
|
|
94
109
|
export {
|
|
95
110
|
AgentActivityPanel,
|
|
@@ -100,6 +115,9 @@ export {
|
|
|
100
115
|
DEFAULT_EFFORT_LEVELS,
|
|
101
116
|
DEFAULT_MENTION_EMPTY_TEXT,
|
|
102
117
|
DEFAULT_MENTION_LIMIT,
|
|
118
|
+
DurableChatCards,
|
|
119
|
+
DurablePlanCard,
|
|
120
|
+
DurablePlanClientError,
|
|
103
121
|
EffortPicker,
|
|
104
122
|
FlowWaterfall,
|
|
105
123
|
INDEX_REFRESH_AFTER_MS,
|
|
@@ -128,20 +146,28 @@ export {
|
|
|
128
146
|
composerAnswerData,
|
|
129
147
|
composerAnswerDeliveries,
|
|
130
148
|
consumeChatStream,
|
|
149
|
+
createDurableInteractionAnswerSubmitter,
|
|
150
|
+
createDurablePlanDecisionClient,
|
|
131
151
|
createInteractionAnswerSubmitter,
|
|
152
|
+
createMemoryInteractionAttemptStore,
|
|
153
|
+
createSessionInteractionAttemptStore,
|
|
132
154
|
dedupeQuestionInteractionsByContent,
|
|
133
155
|
dispatchChatStreamLine,
|
|
156
|
+
durableChatCardsFromParts,
|
|
134
157
|
fieldAcceptsFreeText,
|
|
135
158
|
fieldAnswer,
|
|
159
|
+
fieldValuesFromAnswers,
|
|
136
160
|
fileMentionsToParts,
|
|
137
161
|
formatActivityCost,
|
|
138
162
|
formatActivityDuration,
|
|
139
163
|
formatModelCost,
|
|
140
164
|
formatTokensPerSecond,
|
|
141
165
|
hasSecretField,
|
|
166
|
+
hydrateChatInteractions,
|
|
142
167
|
interactionFromWireRequest,
|
|
143
168
|
interactionPartKey,
|
|
144
169
|
interactionStatusLabels,
|
|
170
|
+
interactionSubmissionSignature,
|
|
145
171
|
interactionTerminalNotes,
|
|
146
172
|
interactionToPersistedPart,
|
|
147
173
|
isLateAnswerableStatus,
|
|
@@ -154,6 +180,7 @@ export {
|
|
|
154
180
|
nextRevealCount,
|
|
155
181
|
noticePart,
|
|
156
182
|
noticePartKey,
|
|
183
|
+
parseInteractionAnswers,
|
|
157
184
|
parseInteractionCancel,
|
|
158
185
|
parseInteractionRequest,
|
|
159
186
|
pendingApprovalOf,
|
|
@@ -163,11 +190,13 @@ export {
|
|
|
163
190
|
resolveChatInteraction,
|
|
164
191
|
responseErrorMessage,
|
|
165
192
|
restoreChatInteractions,
|
|
193
|
+
stampInteractionAnswers,
|
|
166
194
|
streamChatTurn,
|
|
167
195
|
tabTerminalConnectionId,
|
|
168
196
|
terminalizePendingChatInteractions,
|
|
169
197
|
upsertChatInteraction,
|
|
170
198
|
useChatInteractions,
|
|
199
|
+
useDurablePlanFlow,
|
|
171
200
|
useFileMentions,
|
|
172
201
|
usePending,
|
|
173
202
|
usePopover,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-app",
|
|
3
|
-
"version": "0.43.
|
|
3
|
+
"version": "0.43.43",
|
|
4
4
|
"packageManager": "pnpm@10.33.4",
|
|
5
5
|
"description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent→app tool side channel, integration-hub client, per-workspace billing, and crypto — composed over the Tangle agent substrate through typed seams.",
|
|
6
6
|
"keywords": [
|
|
@@ -182,6 +182,16 @@
|
|
|
182
182
|
"import": "./dist/interactions/index.js",
|
|
183
183
|
"default": "./dist/interactions/index.js"
|
|
184
184
|
},
|
|
185
|
+
"./plans": {
|
|
186
|
+
"types": "./dist/plans/index.d.ts",
|
|
187
|
+
"import": "./dist/plans/index.js",
|
|
188
|
+
"default": "./dist/plans/index.js"
|
|
189
|
+
},
|
|
190
|
+
"./durable-chat": {
|
|
191
|
+
"types": "./dist/durable-chat/index.d.ts",
|
|
192
|
+
"import": "./dist/durable-chat/index.js",
|
|
193
|
+
"default": "./dist/durable-chat/index.js"
|
|
194
|
+
},
|
|
185
195
|
"./missions": {
|
|
186
196
|
"types": "./dist/missions/index.d.ts",
|
|
187
197
|
"import": "./dist/missions/index.js",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/interactions/contract.ts"],"sourcesContent":["// Shared contract for agent interaction events (kind: \"question\" et al) — the\n// generalized human-in-the-loop primitive on the chat stream. Framework-agnostic\n// (no server- or React-only imports) so a producer (chat route) and a consumer\n// (stream parser, transcript, question card) agree on one wire shape and one\n// persisted-part shape.\n//\n// An `interaction` event means the run is BLOCKED inside the sidecar's\n// InteractionBroker until the user answers, the agent withdraws the ask\n// (`interaction.cancel`), or the broker times out. A pending interaction is\n// \"waiting on the user\", not \"model working\".\n\nimport {\n InteractionRequestSchema,\n type InteractionData,\n type InteractionField,\n type InteractionOutcome,\n type InteractionRequest,\n} from '@tangle-network/agent-interface'\n\nexport type { InteractionData, InteractionOutcome, InteractionRequest }\n\n// ---------------------------------------------------------------------------\n// Event names\n\n/** Sidecar → client: the agent raised an ask; data = `{ request }`. */\nexport const INTERACTION_EVENT = 'interaction' as const\n/** Sidecar → client: the ask was withdrawn; data = `{ id, reason? }`. */\nexport const INTERACTION_CANCEL_EVENT = 'interaction.cancel' as const\n/** An ask was answered; data = `{ id, status }`. In the wire contract so a\n * server broadcast and a client-local mark share one event name. */\nexport const INTERACTION_RESOLVED_EVENT = 'interaction.resolved' as const\n\n/** Interaction kinds a product typically renders a card for. Anything else is\n * auto-declined by the chat producer's safety net and never reaches a client.\n * A pure default; a product may substitute its own renderable set. */\nconst RENDERABLE_INTERACTION_KINDS: ReadonlySet<string> = new Set(['question', 'plan'])\n\nexport function isRenderableInteractionKind(kind: string): boolean {\n return RENDERABLE_INTERACTION_KINDS.has(kind)\n}\n\n/** Answer/field keys the sidecar will accept: identifier-safe and never a\n * prototype-pollution vector. */\nexport function isSafeInteractionFieldKey(key: string): boolean {\n return /^[A-Za-z0-9_-]+$/.test(key) && key !== '__proto__' && key !== 'constructor' && key !== 'prototype'\n}\n\n// ---------------------------------------------------------------------------\n// Field types\n//\n// `allowCustom` (a select that also accepts a write-in value) is defined by\n// newer agent-interface schemas; older pinned schemas strip unknown keys on\n// parse. The wire/persisted field types below carry the flag so a card can gate\n// its write-in input, and `parseInteractionRequest` returns the RAW payload\n// (schema-validated, not schema-parsed) so the flag survives.\n\nexport type ChatSelectField = Extract<InteractionField, { type: 'select' }> & {\n allowCustom?: boolean\n}\nexport type ChatInteractionField = Exclude<InteractionField, { type: 'select' }> | ChatSelectField\n\n/** `InteractionRequest` whose select fields may carry `allowCustom`. */\nexport type InteractionRequestWire = Omit<InteractionRequest, 'answerSpec'> & {\n answerSpec: { fields: ChatInteractionField[] }\n}\n\n// ---------------------------------------------------------------------------\n// Interaction lifecycle\n\nexport type ChatInteractionStatus = 'pending' | 'answered' | 'declined' | 'cancelled' | 'expired'\n\n/** The client/persisted view of one ask. `fields` come verbatim off the wire. */\nexport interface ChatInteraction {\n id: string\n kind: string\n title: string\n body?: string\n fields: ChatInteractionField[]\n status: ChatInteractionStatus\n /** Set when status came from an `interaction.cancel` (e.g. \"timeout\"). */\n cancelReason?: string\n}\n\nexport function isTerminalInteractionStatus(status: ChatInteractionStatus): boolean {\n return status !== 'pending'\n}\n\n/** Statuses only move forward (pending → terminal); a replayed/stale `pending`\n * must never resurrect a resolved card. */\nexport function canTransitionInteractionStatus(\n from: ChatInteractionStatus,\n to: ChatInteractionStatus,\n): boolean {\n return from === 'pending' && to !== from\n}\n\n/** Maps an `interaction.cancel` reason to the card's terminal status. */\nexport function cancelStatusFor(reason: string | undefined): ChatInteractionStatus {\n return reason === 'timeout' ? 'expired' : 'cancelled'\n}\n\nfunction stableValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(stableValue)\n if (!value || typeof value !== 'object') return value\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, stableValue(nested)]),\n )\n}\n\nfunction normalizedInteractionText(value: string | undefined): string {\n return (value ?? '').replace(/\\s+/g, ' ').trim()\n}\n\n/** Content identity for duplicate safety nets. Excludes volatile ids/statuses. */\nexport function questionInteractionContentSignature(interaction: ChatInteraction): string | null {\n if (interaction.kind !== 'question') return null\n return JSON.stringify(stableValue({\n kind: interaction.kind,\n title: normalizedInteractionText(interaction.title),\n body: normalizedInteractionText(interaction.body),\n fields: interaction.fields,\n }))\n}\n\nexport function dedupeQuestionInteractionsByContent(interactions: ChatInteraction[]): ChatInteraction[] {\n const seen = new Set<string>()\n return interactions.filter((interaction) => {\n const signature = questionInteractionContentSignature(interaction)\n if (!signature) return true\n if (seen.has(signature)) return false\n seen.add(signature)\n return true\n })\n}\n\n// ---------------------------------------------------------------------------\n// Wire parsing — typed outcomes, fail loud at the caller (log + skip; never a\n// half-rendered card).\n\nexport type ParseInteractionResult =\n | { succeeded: true; value: InteractionRequestWire }\n | { succeeded: false; error: string }\n\n/** Parses an `interaction` event's data (`{ request }`). Validates the shape\n * with the agent-interface schema but returns the raw request so a field a\n * pinned schema predates (`allowCustom`) survives. */\nexport function parseInteractionRequest(data: Record<string, unknown> | undefined): ParseInteractionResult {\n const request = data?.request\n if (!request || typeof request !== 'object') {\n return { succeeded: false, error: 'interaction event carried no request object' }\n }\n const validation = InteractionRequestSchema.safeParse(request)\n if (!validation.success) {\n return { succeeded: false, error: `malformed interaction request: ${validation.error.message}` }\n }\n return { succeeded: true, value: request as InteractionRequestWire }\n}\n\nexport interface InteractionCancelData {\n id: string\n reason?: string\n}\n\nexport function parseInteractionCancel(\n data: Record<string, unknown> | undefined,\n): { succeeded: true; value: InteractionCancelData } | { succeeded: false; error: string } {\n const id = typeof data?.id === 'string' && data.id ? data.id : null\n if (!id) return { succeeded: false, error: 'interaction.cancel event carried no id' }\n const reason = typeof data?.reason === 'string' && data.reason ? data.reason : undefined\n return { succeeded: true, value: { id, ...(reason ? { reason } : {}) } }\n}\n\n// ---------------------------------------------------------------------------\n// Composer-as-answer delivery: while asks are pending the composer never\n// blocks — typed text is delivered verbatim to every open ask and the agent\n// decides what it means. No interpretation here; the sidecar validates answers\n// fail-closed (invalid free text on an option-only ask → 400).\n\nexport function fieldAcceptsFreeText(field: ChatInteractionField): boolean {\n if (field.type === 'text') return true\n if (field.type === 'select') return (field as ChatSelectField).allowCustom === true\n return false\n}\n\nexport interface ComposerAnswerDelivery {\n interactionId: string\n field: ChatInteractionField\n}\n\n/** One delivery per pending ask: the first free-text-capable field, else the\n * first field. Zero-field asks are skipped (nothing to carry the text). */\nexport function composerAnswerDeliveries(pending: ChatInteraction[]): ComposerAnswerDelivery[] {\n const deliveries: ComposerAnswerDelivery[] = []\n for (const interaction of pending) {\n // Only questions take a composer-routed answer. A non-question ask (a plan)\n // is POSTed as outcome:\"accepted\" when answered — routing composer text to\n // it would silently APPROVE it. Approval is an explicit card click, so the\n // composer skips it and the plan card stays the only path.\n if (interaction.kind !== 'question') continue\n const field = interaction.fields.find(fieldAcceptsFreeText) ?? interaction.fields[0]\n if (!field) continue\n deliveries.push({ interactionId: interaction.id, field })\n }\n return deliveries\n}\n\n/** Shapes composer text into the respond payload for the routed field\n * (select answers are string arrays on the wire; text answers are strings). */\nexport function composerAnswerData(field: ChatInteractionField, text: string): InteractionData {\n return { [field.name]: field.type === 'select' ? [text] : text }\n}\n\n// ---------------------------------------------------------------------------\n// Part keys + codecs (persisted `messages.parts` entries and live stream parts\n// share these shapes).\n\nexport function interactionPartKey(id: string): string {\n return `interaction:${id}`\n}\n\nexport function noticePartKey(id: string): string {\n return `notice:${id}`\n}\n\nexport type NoticeKind = 'warning' | 'auto-declined'\n\n/**\n * Persisted-part shapes the codecs below produce — the SAME rows\n * `/chat-store`'s `ChatInteractionPart`/`ChatNoticePart` store, typed at the\n * source so a product pushing them into a `ChatMessagePart[]` transcript needs\n * no cast. Type aliases (not interfaces) on purpose: the implicit index\n * signature keeps them assignable to the `Record<string, unknown>` these\n * codecs previously returned, so existing consumers stay source-compatible.\n */\nexport type InteractionPersistedPart = {\n type: 'interaction'\n id: string\n kind: string\n title: string\n body?: string\n answerSpec: { fields: ChatInteractionField[] }\n status: ChatInteractionStatus\n cancelReason?: string\n}\n\nexport type NoticePersistedPart = {\n type: 'notice'\n id: string\n noticeKind: NoticeKind\n text: string\n}\n\n/** Builds the persisted/streamed `notice` part — a one-line transcript notice\n * explaining an out-of-band event (warning, auto-declined interaction). */\nexport function noticePart(noticeKind: NoticeKind, id: string, text: string): NoticePersistedPart {\n return { type: 'notice', id, noticeKind, text }\n}\n\n/** Reads a wire request into the client's pending `ChatInteraction`. */\nexport function interactionFromWireRequest(request: InteractionRequestWire): ChatInteraction {\n return {\n id: request.id,\n kind: request.kind,\n title: request.title,\n ...(request.body ? { body: request.body } : {}),\n fields: request.answerSpec.fields,\n status: 'pending',\n }\n}\n\n/** Builds the persisted/streamed `interaction` part from a wire request. */\nexport function interactionToPersistedPart(\n request: InteractionRequestWire,\n status: ChatInteractionStatus,\n cancelReason?: string,\n): InteractionPersistedPart {\n return {\n type: 'interaction',\n id: request.id,\n kind: request.kind,\n title: request.title,\n ...(request.body ? { body: request.body } : {}),\n answerSpec: { fields: request.answerSpec.fields },\n status,\n ...(cancelReason ? { cancelReason } : {}),\n }\n}\n\n/** Reads a persisted/streamed `interaction` part back into a `ChatInteraction`.\n * Returns null (caller logs) when the part is not one of ours. */\nexport function persistedPartToInteraction(part: Record<string, unknown>): ChatInteraction | null {\n if (String(part.type ?? '') !== 'interaction') return null\n const id = typeof part.id === 'string' && part.id ? part.id : null\n const kind = typeof part.kind === 'string' && part.kind ? part.kind : null\n const title = typeof part.title === 'string' ? part.title : ''\n const answerSpec = part.answerSpec as { fields?: unknown } | undefined\n const fields = Array.isArray(answerSpec?.fields) ? (answerSpec.fields as ChatInteractionField[]) : null\n const status = part.status as ChatInteractionStatus | undefined\n const validStatus = status && ['pending', 'answered', 'declined', 'cancelled', 'expired'].includes(status)\n if (!id || !kind || !fields || !validStatus) return null\n return {\n id,\n kind,\n title,\n ...(typeof part.body === 'string' && part.body ? { body: part.body } : {}),\n fields,\n status,\n ...(typeof part.cancelReason === 'string' && part.cancelReason ? { cancelReason: part.cancelReason } : {}),\n }\n}\n"],"mappings":"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","names":[]}
|