@datalayer/agent-runtimes 1.3.106 → 1.3.109

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.
@@ -3,10 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  * Copyright (c) 2025-2026 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { useEffect, useState } from 'react';
6
+ import { useEffect, useMemo, useRef, useState, } from 'react';
7
7
  import { Box } from '@datalayer/primer-addons';
8
- import { Button, Heading, Label, Text } from '@primer/react';
9
- import { buildDirectSignInRequest, buildSessionAssertionClaims, parseDiscoveryDocument, } from '@datalayer/personal-agent-protocol';
8
+ import { contribution, definePlugin } from '@datalayer/reactor';
9
+ import { Button, Heading, IconButton, Label, Text } from '@primer/react';
10
+ import { ChevronLeftIcon, ChevronRightIcon } from '@primer/octicons-react';
11
+ import { ConversationClient, DpopFetchClient, DpopProofProviders, SecretValue, buildPapReactor, buildDirectSignInRequest, buildSessionAssertionClaims, parseDiscoveryDocument, } from '@datalayer/personal-agent-protocol';
12
+ import { ChatMessageList } from '../../chat/messages/ChatMessageList';
10
13
  import { PapExamplePage } from './PapExamplePage';
11
14
  const conversationStart = [
12
15
  {
@@ -125,18 +128,31 @@ Location: https://atlas.example/oauth/callback
125
128
  DPoP: [fresh proof]
126
129
  Content-Type: application/json
127
130
 
128
- { "message_id": "msg_offer", "text": "The user has signed in." }`,
129
- response: `200 OK
130
131
  {
131
- "text": "Choose an even exchange or refund.",
132
- "data": { "options": ["exchange", "refund"] },
133
- "responder": { "type": "ai", "name": "Northstar" }
132
+ "message": {
133
+ "id": "msg_offer",
134
+ "sender": "agent",
135
+ "text": "The user has signed in. Continue the return request."
136
+ }
137
+ }`,
138
+ response: `202 Accepted
139
+ {
140
+ "conversation_id": "cnv_return",
141
+ "events": [{
142
+ "type": "message",
143
+ "message": {
144
+ "role": "company",
145
+ "text": "Choose an even exchange or refund."
146
+ }
147
+ }],
148
+ "status": "idle",
149
+ "responder": "agent"
134
150
  }`,
135
151
  },
136
152
  {
137
153
  title: 'The exchange is set',
138
154
  summary: "Atlas passes on the user's exact choice. Northstar confirms the exchange and the conversation closes with a durable result.",
139
- channel: 'Operation + result',
155
+ channel: 'Conversation result',
140
156
  messages: [
141
157
  ...conversationStart,
142
158
  { speaker: 'company', text: 'Please sign in so I can find the order.' },
@@ -157,18 +173,30 @@ Content-Type: application/json
157
173
  { label: 'Conversation', value: 'Closed' },
158
174
  ],
159
175
  method: 'POST',
160
- target: 'https://api.northstar.example/poppy/operations/op_exchange/confirm',
161
- detailLabel: 'Exact user-authorized action',
176
+ target: 'https://api.northstar.example/poppy/conversations/cnv_return/messages',
177
+ detailLabel: 'Atlas ↔ company agent',
162
178
  request: `Authorization: DPoP [host-only Session Token]
163
179
  DPoP: [fresh proof]
164
- Idempotency-Key: msg_exchange_choice
180
+ Content-Type: application/json
165
181
 
166
- { "revision": 1, "choice": "exchange" }`,
167
- response: `200 OK
168
182
  {
169
- "status": "succeeded",
170
- "result": { "return_code": "NRT-7K4Q" },
171
- "conversation_status": "closed"
183
+ "message": {
184
+ "id": "msg_exchange_choice",
185
+ "sender": "agent",
186
+ "text": "The user chose the even exchange."
187
+ }
188
+ }`,
189
+ response: `202 Accepted
190
+ {
191
+ "conversation_id": "cnv_return",
192
+ "events": [{
193
+ "type": "message",
194
+ "message": {
195
+ "role": "company",
196
+ "text": "The exchange is arranged."
197
+ }
198
+ }],
199
+ "status": "closed"
172
200
  }`,
173
201
  },
174
202
  ];
@@ -177,7 +205,24 @@ const speakerName = {
177
205
  atlas: 'Atlas',
178
206
  company: 'Northstar',
179
207
  };
180
- const validateJourneyFixtures = async () => {
208
+ const companyReply = (response) => {
209
+ if (!('events' in response)) {
210
+ throw new Error('Northstar returned a receipt without a message');
211
+ }
212
+ const reply = response.events.find(event => event.type === 'message' && event.message?.role === 'company')?.message?.text;
213
+ if (!reply) {
214
+ throw new Error('Northstar returned no company reply');
215
+ }
216
+ return reply;
217
+ };
218
+ /**
219
+ * Run the visible transcript through the actual PAP conversation client.
220
+ *
221
+ * The company side is deliberately an in-page deterministic simulator: this
222
+ * makes the example runnable without an account while ensuring that requests,
223
+ * proof-bound transport, response parsing, and messages all use the real SDK.
224
+ */
225
+ const runJourney = async () => {
181
226
  parseDiscoveryDocument({
182
227
  protocol_version: '0.1',
183
228
  organization: { name: 'Northstar Outfitters', domain: 'northstar.example' },
@@ -207,42 +252,182 @@ const validateJourneyFixtures = async () => {
207
252
  redirectUri: 'https://atlas.example/oauth/callback',
208
253
  scopes: ['orders:read', 'orders:return'],
209
254
  });
255
+ const replies = [
256
+ 'I can help with that. Please sign in so I can find the order.',
257
+ 'Order NS-48213 can be exchanged for the warmer Summit jacket at no extra cost, or refunded in full.',
258
+ 'The exchange is arranged. The Summit jacket ships tomorrow, and return code NRT-7K4Q is ready.',
259
+ ];
260
+ let requestIndex = 0;
261
+ const localCompanyFetch = async (_input, init) => {
262
+ const index = requestIndex++;
263
+ const reply = replies[index];
264
+ if (!reply) {
265
+ return new Response(JSON.stringify({ error: 'unexpected_request' }), {
266
+ status: 400,
267
+ headers: { 'content-type': 'application/json' },
268
+ });
269
+ }
270
+ const incoming = JSON.parse(String(init?.body ?? '{}'));
271
+ return new Response(JSON.stringify({
272
+ conversation_id: 'cnv_return',
273
+ events: [
274
+ {
275
+ id: `evt_${index + 1}`,
276
+ type: 'message',
277
+ created_at: `2026-10-10T09:4${index}:00Z`,
278
+ message: {
279
+ id: `reply_${index + 1}`,
280
+ sender: 'agent',
281
+ role: 'company',
282
+ text: reply,
283
+ context: { in_reply_to: incoming.message?.id ?? 'unknown' },
284
+ },
285
+ },
286
+ ],
287
+ cursor: `cursor_${index + 1}`,
288
+ has_more: false,
289
+ status: index === replies.length - 1 ? 'closed' : 'idle',
290
+ responder: 'agent',
291
+ }), {
292
+ status: index === 0 ? 201 : 202,
293
+ headers: { 'content-type': 'application/json' },
294
+ });
295
+ };
296
+ const securityPlugin = definePlugin({
297
+ name: '@datalayer/loop-example-pap-guided-security',
298
+ contributes: [
299
+ contribution(DpopProofProviders, {
300
+ async create() {
301
+ return new SecretValue(`host-only-proof-${requestIndex + 1}`);
302
+ },
303
+ }, { id: 'guided-example-dpop' }),
304
+ ],
305
+ });
306
+ const reactor = buildPapReactor({ fetch: localCompanyFetch }, [
307
+ securityPlugin,
308
+ ]);
309
+ try {
310
+ const transport = new DpopFetchClient({
311
+ reactor,
312
+ fetch: localCompanyFetch,
313
+ });
314
+ const conversations = new ConversationClient({ reactor, transport });
315
+ const session = (signedIn) => ({
316
+ accessToken: new SecretValue(signedIn ? 'host-only-signed-in-token' : 'host-only-session-token'),
317
+ tokenType: 'DPoP',
318
+ expiresIn: 300,
319
+ scopes: signedIn ? ['orders:read', 'orders:return'] : ['poppy:read'],
320
+ sessionId: 'ses_guided_example',
321
+ signedIn,
322
+ });
323
+ const requestOptions = (token) => ({
324
+ token,
325
+ tenantId: 'guided-example',
326
+ pairwiseUserId: 'usr_host_only_journey',
327
+ issuer: 'https://auth.northstar.example',
328
+ wait: 0,
329
+ });
330
+ const endpoint = 'https://api.northstar.example/poppy/conversations';
331
+ const started = await conversations.start(endpoint, {
332
+ id: conversations.generateMessageId(),
333
+ sender: 'agent',
334
+ text: conversationStart[0]?.text,
335
+ }, requestOptions(session(false)));
336
+ const signInReply = companyReply(started);
337
+ const offer = companyReply(await conversations.send(endpoint, 'cnv_return', {
338
+ id: conversations.generateMessageId(),
339
+ sender: 'agent',
340
+ text: 'The user has signed in. Continue the return request.',
341
+ }, requestOptions(session(true))));
342
+ const confirmation = companyReply(await conversations.send(endpoint, 'cnv_return', {
343
+ id: conversations.generateMessageId(),
344
+ sender: 'agent',
345
+ text: 'The user chose the even exchange.',
346
+ }, requestOptions(session(true))));
347
+ const signedInMessages = [
348
+ ...conversationStart,
349
+ { speaker: 'company', text: signInReply },
350
+ { speaker: 'user', text: "I'm signed in." },
351
+ { speaker: 'atlas', text: "Thanks. I'll continue the same request." },
352
+ { speaker: 'company', text: offer },
353
+ ];
354
+ return [
355
+ conversationStart,
356
+ [...conversationStart, { speaker: 'company', text: signInReply }],
357
+ conversationStart,
358
+ signedInMessages,
359
+ [
360
+ ...signedInMessages,
361
+ { speaker: 'user', text: "Let's do the exchange." },
362
+ { speaker: 'company', text: confirmation },
363
+ ],
364
+ ];
365
+ }
366
+ finally {
367
+ reactor.stop();
368
+ }
369
+ };
370
+ const ChatPhone = ({ step, messages, onAuthorized }) => {
371
+ const messagesEndRef = useRef(null);
372
+ const displayItems = useMemo(() => messages.map((message, index) => ({
373
+ id: `guided-${step.channel}-${index}`,
374
+ role: message.speaker === 'user' ? 'user' : 'assistant',
375
+ content: message.text,
376
+ createdAt: new Date(`2026-10-10T09:4${index}:00Z`),
377
+ speaker: {
378
+ id: message.speaker,
379
+ name: speakerName[message.speaker],
380
+ role: message.speaker === 'atlas'
381
+ ? 'personal agent'
382
+ : message.speaker === 'company'
383
+ ? 'company agent'
384
+ : 'person',
385
+ tone: message.speaker === 'company' ? 'success' : 'accent',
386
+ initials: message.speaker === 'company'
387
+ ? 'N'
388
+ : message.speaker === 'atlas'
389
+ ? 'A'
390
+ : 'Y',
391
+ },
392
+ })), [messages, step.channel]);
393
+ return (_jsxs(Box, { width: "100%", maxWidth: 340, height: 560, mx: "auto", border: "7px solid", borderColor: "fg.default", borderRadius: 20, bg: "canvas.default", overflow: "hidden", boxShadow: "shadow.large", display: "flex", flexDirection: "column", children: [_jsxs(Box, { px: 3, py: 2, display: "flex", justifyContent: "space-between", borderBottom: "1px solid", borderColor: "border.default", children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 600 }, children: "9:41" }), _jsx(Text, { sx: { fontSize: 0, fontWeight: 600 }, children: "5G \u00B7 92%" })] }), step.phoneMode === 'signin' ? (_jsxs(Box, { flex: 1, display: "flex", flexDirection: "column", children: [_jsx(Box, { bg: "success.emphasis", color: "fg.onEmphasis", p: 3, children: _jsx(Text, { sx: { fontWeight: 700 }, children: "N \u00B7 Northstar Outfitters" }) }), _jsxs(Box, { p: 3, display: "flex", flexDirection: "column", gap: 3, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "auth.northstar.example" }), _jsx(Heading, { as: "h2", sx: { fontSize: 3 }, children: "Let Atlas access your order?" }), _jsx(Text, { children: "Atlas can read order NS-48213 and arrange the return you requested." }), _jsxs(Box, { p: 3, bg: "canvas.subtle", borderRadius: 2, children: [_jsx(Text, { sx: { fontWeight: 600 }, children: "Requested access" }), _jsx(Text, { as: "p", sx: { mt: 1, color: 'fg.muted' }, children: "Read this order \u00B7 Arrange one return" })] }), _jsx(Button, { variant: "primary", block: true, onClick: onAuthorized, children: "Authorize Atlas" }), _jsx(Text, { textAlign: "center", sx: { fontSize: 0, color: 'fg.muted' }, children: "Passwords and account credentials stay with Northstar." })] })] })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { px: 3, py: 2, display: "flex", gap: 2, alignItems: "center", children: [_jsx(Box, { width: 30, height: 30, borderRadius: "50%", bg: "accent.emphasis", color: "fg.onEmphasis", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 700, children: "A" }), _jsxs(Box, { children: [_jsx(Text, { as: "div", sx: { fontWeight: 700 }, children: "Atlas" }), _jsx(Text, { as: "div", sx: { fontSize: 0, color: 'fg.muted' }, children: "Your personal agent" })] })] }), _jsx(Box, { flex: 1, overflow: "auto", bg: "canvas.subtle", children: _jsx(ChatMessageList, { displayItems: displayItems, isLoading: false, isStreaming: false, showLoadingIndicator: false, hideMessagesAfterToolUI: false, avatarConfig: {
394
+ userAvatar: 'Y',
395
+ assistantAvatar: 'A',
396
+ showAvatars: true,
397
+ avatarSize: 26,
398
+ userAvatarBg: 'accent.muted',
399
+ assistantAvatarBg: 'accent.emphasis',
400
+ }, padding: 2, emptyContent: null, messagesEndRef: messagesEndRef, onRespond: async () => undefined, density: "comfortable" }) })] }))] }));
210
401
  };
211
- const ChatPhone = ({ step, onAuthorized }) => (_jsxs(Box, { width: "100%", maxWidth: 340, height: 560, mx: "auto", border: "10px solid", borderColor: "fg.default", borderRadius: 36, bg: "canvas.default", overflow: "hidden", boxShadow: "shadow.large", display: "flex", flexDirection: "column", children: [_jsxs(Box, { px: 3, py: 2, display: "flex", justifyContent: "space-between", borderBottom: "1px solid", borderColor: "border.default", children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 600 }, children: "9:41" }), _jsx(Text, { sx: { fontSize: 0, fontWeight: 600 }, children: "5G \u00B7 92%" })] }), step.phoneMode === 'signin' ? (_jsxs(Box, { flex: 1, display: "flex", flexDirection: "column", children: [_jsx(Box, { bg: "success.emphasis", color: "fg.onEmphasis", p: 3, children: _jsx(Text, { sx: { fontWeight: 700 }, children: "N \u00B7 Northstar Outfitters" }) }), _jsxs(Box, { p: 3, display: "flex", flexDirection: "column", gap: 3, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "auth.northstar.example" }), _jsx(Heading, { as: "h2", sx: { fontSize: 3 }, children: "Let Atlas access your order?" }), _jsx(Text, { children: "Atlas can read order NS-48213 and arrange the return you requested." }), _jsxs(Box, { p: 3, bg: "canvas.subtle", borderRadius: 2, children: [_jsx(Text, { sx: { fontWeight: 600 }, children: "Requested access" }), _jsx(Text, { as: "p", sx: { mt: 1, color: 'fg.muted' }, children: "Read this order \u00B7 Arrange one return" })] }), _jsx(Button, { variant: "primary", block: true, onClick: onAuthorized, children: "Authorize Atlas" }), _jsx(Text, { textAlign: "center", sx: { fontSize: 0, color: 'fg.muted' }, children: "Passwords and account credentials stay with Northstar." })] })] })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { px: 3, py: 2, display: "flex", gap: 2, alignItems: "center", children: [_jsx(Box, { width: 30, height: 30, borderRadius: "50%", bg: "accent.emphasis", color: "fg.onEmphasis", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 700, children: "A" }), _jsxs(Box, { children: [_jsx(Text, { as: "div", sx: { fontWeight: 700 }, children: "Atlas" }), _jsx(Text, { as: "div", sx: { fontSize: 0, color: 'fg.muted' }, children: "Your personal agent" })] })] }), _jsx(Box, { flex: 1, overflow: "auto", bg: "canvas.subtle", p: 2, display: "flex", flexDirection: "column", gap: 2, children: step.messages.map((message, index) => {
212
- const fromUser = message.speaker === 'user';
213
- return (_jsxs(Box, { alignSelf: fromUser ? 'flex-end' : 'flex-start', maxWidth: "88%", px: 2, py: 2, borderRadius: 3, color: fromUser ? 'fg.onEmphasis' : 'fg.default', bg: fromUser ? 'accent.emphasis' : 'canvas.default', border: fromUser ? 'none' : '1px solid', borderColor: "border.default", children: [!fromUser ? (_jsx(Text, { as: "div", sx: {
214
- mb: 1,
215
- fontSize: 0,
216
- fontWeight: 700,
217
- color: message.speaker === 'company'
218
- ? 'success.fg'
219
- : 'accent.fg',
220
- }, children: speakerName[message.speaker] })) : null, _jsx(Text, { sx: { fontSize: 1 }, children: message.text })] }, `${message.speaker}-${index}`));
221
- }) })] }))] }));
222
402
  /** A five-step user, company, and wire-level PAP walkthrough. */
223
403
  const PapGuidedJourneyView = () => {
224
404
  const [stepIndex, setStepIndex] = useState(0);
225
405
  const [detailsOpen, setDetailsOpen] = useState(true);
226
- const [fixtureState, setFixtureState] = useState('checking');
406
+ const [interactionState, setInteractionState] = useState('checking');
407
+ const [liveMessages, setLiveMessages] = useState(null);
227
408
  const step = STEPS[stepIndex] ?? STEPS[0];
409
+ const messages = liveMessages?.[stepIndex] ?? step.messages;
228
410
  useEffect(() => {
229
- void validateJourneyFixtures()
230
- .then(() => setFixtureState('valid'))
231
- .catch(() => setFixtureState('invalid'));
411
+ void runJourney()
412
+ .then(result => {
413
+ setLiveMessages(result);
414
+ setInteractionState('valid');
415
+ })
416
+ .catch(() => setInteractionState('invalid'));
232
417
  }, []);
233
418
  const go = (next) => {
234
419
  setStepIndex(Math.max(0, Math.min(STEPS.length - 1, next)));
235
420
  };
236
- return (_jsxs(PapExamplePage, { eyebrow: "PAP \u00B7 Guided interaction", title: "A return, from first request to confirmed exchange", description: "Follow what the user experiences, what the company is allowed to see, and which PAP exchange moves the work forward. The story uses SDK-validated discovery, Session, and Direct Sign-In fixtures; protocol credentials are always represented as host-only placeholders.", maxWidth: 1180, children: [_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 2, flexWrap: "wrap", p: 3, bg: "canvas.default", border: "1px solid", borderColor: "border.default", borderRadius: 2, children: [_jsxs(Box, { children: [_jsx(Label, { variant: "accent", children: "Company agent \u00B7 Direct Sign-In" }), _jsx(Heading, { as: "h2", sx: { mt: 1, fontSize: 2 }, children: "The jacket is not warm enough" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Atlas finds Northstar, keeps one Session, and asks before acting." })] }), _jsx(Label, { variant: fixtureState === 'valid' ? 'success' : 'secondary', children: fixtureState === 'valid'
237
- ? 'SDK fixtures validated'
238
- : fixtureState === 'invalid'
239
- ? 'Fixture validation failed'
240
- : 'Validating SDK fixtures…' })] }), _jsx(Box, { display: "flex", gap: 2, flexWrap: "wrap", "aria-label": "Journey steps", children: STEPS.map((candidate, index) => (_jsxs(Button, { variant: index === stepIndex ? 'primary' : 'default', onClick: () => go(index), children: [index + 1, ". ", candidate.channel] }, candidate.title))) }), _jsxs(Box, { display: "grid", sx: {
421
+ return (_jsxs(PapExamplePage, { eyebrow: "PAP \u00B7 Guided interaction", title: "A return, from first request to confirmed exchange", description: "Follow what the user experiences, what the company is allowed to see, and which PAP exchange moves the work forward. Northstar's replies are parsed from real PAP ConversationClient requests against a deterministic in-page simulator; discovery and sign-in values use the SDK too. No external retailer, account, or language model is implied.", maxWidth: 1180, children: [_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 2, flexWrap: "wrap", p: 3, bg: "canvas.default", border: "1px solid", borderColor: "border.default", borderRadius: 2, children: [_jsxs(Box, { children: [_jsx(Label, { variant: "accent", children: "Company agent \u00B7 Direct Sign-In" }), _jsx(Heading, { as: "h2", sx: { mt: 1, fontSize: 2 }, children: "The jacket is not warm enough" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Atlas finds Northstar, keeps one Session, and asks before acting." })] }), _jsx(Label, { variant: interactionState === 'valid' ? 'success' : 'secondary', children: interactionState === 'valid'
422
+ ? 'Live local PAP exchange complete'
423
+ : interactionState === 'invalid'
424
+ ? 'Local PAP exchange failed'
425
+ : 'Running local PAP exchange…' })] }), _jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsx(IconButton, { icon: ChevronLeftIcon, "aria-label": "Previous journey tab", disabled: stepIndex === 0, onClick: () => go(stepIndex - 1) }), _jsx(Box, { display: "flex", gap: 2, overflow: "auto", flex: 1, "aria-label": "Journey steps", children: STEPS.map((candidate, index) => (_jsxs(Button, { variant: index === stepIndex ? 'primary' : 'default', onClick: () => go(index), sx: { flexShrink: 0 }, children: [index + 1, ". ", candidate.channel] }, candidate.title))) }), _jsx(IconButton, { icon: ChevronRightIcon, "aria-label": "Next journey tab", disabled: stepIndex === STEPS.length - 1, onClick: () => go(stepIndex + 1) })] }), _jsxs(Box, { display: "grid", sx: {
241
426
  gridTemplateColumns: [
242
427
  '1fr',
243
428
  'minmax(300px, 0.78fr) minmax(460px, 1.4fr)',
244
429
  ],
245
- }, gap: 4, alignItems: "start", children: [_jsx(ChatPhone, { step: step, onAuthorized: () => go(stepIndex + 1) }), _jsxs(Box, { minHeight: 560, bg: "canvas.default", border: "1px solid", borderColor: "border.default", borderRadius: 2, overflow: "hidden", display: "flex", flexDirection: "column", children: [_jsxs(Box, { p: 3, borderBottom: "1px solid", borderColor: "border.default", children: [_jsxs(Text, { as: "div", sx: { fontSize: 0, color: 'fg.muted' }, children: ["Step ", stepIndex + 1, " of ", STEPS.length, " \u00B7 ", step.channel] }), _jsx(Heading, { as: "h2", sx: { mt: 1, fontSize: 3 }, children: step.title }), _jsx(Text, { as: "p", sx: { mt: 2, color: 'fg.muted' }, children: step.summary })] }), _jsxs(Box, { p: 3, display: "flex", flexDirection: "column", gap: 3, children: [_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsx(Box, { width: 24, height: 24, borderRadius: 1, bg: "success.emphasis", color: "fg.onEmphasis", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 700, children: "N" }), _jsx(Text, { sx: { fontWeight: 700 }, children: "What Northstar sees" })] }), _jsx(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, bg: "canvas.subtle", px: 3, children: step.facts.map(fact => (_jsxs(Box, { display: "flex", justifyContent: "space-between", gap: 3, py: 2, borderBottom: "1px solid", borderColor: "border.muted", sx: { '&:last-child': { borderBottom: 0 } }, children: [_jsx(Text, { sx: { color: 'fg.muted' }, children: fact.label }), _jsx(Text, { textAlign: "right", sx: { fontWeight: 600 }, children: fact.value })] }, fact.label))) }), _jsxs(Button, { variant: "invisible", sx: { alignSelf: 'flex-start', px: 0 }, onClick: () => setDetailsOpen(open => !open), children: ['{ }', " ", detailsOpen ? 'Hide' : 'Show', " protocol details"] }), detailsOpen ? (_jsxs(Box, { bg: "#0d1117", color: "#c9d1d9", borderRadius: 2, overflow: "hidden", children: [_jsxs(Box, { px: 3, py: 2, display: "flex", justifyContent: "space-between", gap: 2, borderBottom: "1px solid", borderColor: "#30363d", children: [_jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: step.detailLabel }), _jsx(Label, { variant: "secondary", children: step.channel })] }), _jsxs(Box, { p: 3, maxHeight: 270, overflow: "auto", children: [_jsxs(Text, { as: "div", sx: {
430
+ }, gap: 4, alignItems: "start", children: [_jsx(ChatPhone, { step: step, messages: messages, onAuthorized: () => go(stepIndex + 1) }), _jsxs(Box, { minHeight: 560, bg: "canvas.default", border: "1px solid", borderColor: "border.default", borderRadius: 2, overflow: "hidden", display: "flex", flexDirection: "column", children: [_jsxs(Box, { p: 3, borderBottom: "1px solid", borderColor: "border.default", children: [_jsxs(Text, { as: "div", sx: { fontSize: 0, color: 'fg.muted' }, children: ["Step ", stepIndex + 1, " of ", STEPS.length, " \u00B7 ", step.channel] }), _jsx(Heading, { as: "h2", sx: { mt: 1, fontSize: 3 }, children: step.title }), _jsx(Text, { as: "p", sx: { mt: 2, color: 'fg.muted' }, children: step.summary })] }), _jsxs(Box, { p: 3, display: "flex", flexDirection: "column", gap: 3, children: [_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsx(Box, { width: 24, height: 24, borderRadius: 1, bg: "success.emphasis", color: "fg.onEmphasis", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 700, children: "N" }), _jsx(Text, { sx: { fontWeight: 700 }, children: "What Northstar sees" })] }), _jsx(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, bg: "canvas.subtle", px: 3, children: step.facts.map(fact => (_jsxs(Box, { display: "flex", justifyContent: "space-between", gap: 3, py: 2, borderBottom: "1px solid", borderColor: "border.muted", sx: { '&:last-child': { borderBottom: 0 } }, children: [_jsx(Text, { sx: { color: 'fg.muted' }, children: fact.label }), _jsx(Text, { textAlign: "right", sx: { fontWeight: 600 }, children: fact.value })] }, fact.label))) }), _jsxs(Button, { variant: "invisible", sx: { alignSelf: 'flex-start', px: 0 }, onClick: () => setDetailsOpen(open => !open), children: ['{ }', " ", detailsOpen ? 'Hide' : 'Show', " protocol details"] }), detailsOpen ? (_jsxs(Box, { bg: "#0d1117", color: "#c9d1d9", borderRadius: 2, overflow: "hidden", children: [_jsxs(Box, { px: 3, py: 2, display: "flex", justifyContent: "space-between", gap: 2, borderBottom: "1px solid", borderColor: "#30363d", children: [_jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: step.detailLabel }), _jsx(Label, { variant: "secondary", children: step.channel })] }), _jsxs(Box, { p: 3, maxHeight: 270, overflow: "auto", children: [_jsxs(Text, { as: "div", sx: {
246
431
  fontFamily: 'mono',
247
432
  fontWeight: 700,
248
433
  color: '#ff7b72',
@@ -42,6 +42,7 @@ export const SERVER_ACTIONS = {
42
42
  default: [],
43
43
  tools: {},
44
44
  conditions: {},
45
+ signs: {},
45
46
  },
46
47
  chart: {
47
48
  checked: '2026-10-02',
@@ -76,6 +77,7 @@ export const SERVER_ACTIONS = {
76
77
  generate_spreadsheet: ['read'],
77
78
  },
78
79
  conditions: {},
80
+ signs: {},
79
81
  },
80
82
  datalayer: {
81
83
  checked: '2026-10-05',
@@ -227,6 +229,7 @@ export const SERVER_ACTIONS = {
227
229
  mount_local_folder: ['read'],
228
230
  },
229
231
  conditions: {},
232
+ signs: {},
230
233
  },
231
234
  earthdata: {
232
235
  checked: '2026-10-07',
@@ -237,11 +240,13 @@ export const SERVER_ACTIONS = {
237
240
  download_earth_data_granules: ['write'],
238
241
  },
239
242
  conditions: {},
243
+ signs: {},
240
244
  },
241
245
  eurus: {
242
246
  default: [],
243
247
  tools: {},
244
248
  conditions: {},
249
+ signs: {},
245
250
  },
246
251
  filesystem: {
247
252
  checked: '2026-10-02',
@@ -263,11 +268,13 @@ export const SERVER_ACTIONS = {
263
268
  list_allowed_directories: ['read'],
264
269
  },
265
270
  conditions: {},
271
+ signs: {},
266
272
  },
267
273
  github: {
268
274
  default: [],
269
275
  tools: {},
270
276
  conditions: {},
277
+ signs: {},
271
278
  },
272
279
  'google-workspace': {
273
280
  checked: '2026-10-02',
@@ -536,21 +543,29 @@ export const SERVER_ACTIONS = {
536
543
  },
537
544
  ],
538
545
  },
546
+ signs: {
547
+ send_gmail_message: 'body',
548
+ draft_gmail_message: 'body',
549
+ send_message: 'message_text',
550
+ },
539
551
  },
540
552
  huggingface: {
541
553
  default: [],
542
554
  tools: {},
543
555
  conditions: {},
556
+ signs: {},
544
557
  },
545
558
  kaggle: {
546
559
  default: [],
547
560
  tools: {},
548
561
  conditions: {},
562
+ signs: {},
549
563
  },
550
564
  odoo: {
551
565
  default: [],
552
566
  tools: {},
553
567
  conditions: {},
568
+ signs: {},
554
569
  },
555
570
  'odoo-accounting': {
556
571
  checked: '2026-10-05',
@@ -607,11 +622,13 @@ export const SERVER_ACTIONS = {
607
622
  odoo_accounting_set_tax_grids: ['write'],
608
623
  },
609
624
  conditions: {},
625
+ signs: {},
610
626
  },
611
627
  salesforce: {
612
628
  default: [],
613
629
  tools: {},
614
630
  conditions: {},
631
+ signs: {},
615
632
  },
616
633
  slack: {
617
634
  checked: '2026-10-02',
@@ -627,6 +644,10 @@ export const SERVER_ACTIONS = {
627
644
  slack_get_user_profile: ['read'],
628
645
  },
629
646
  conditions: {},
647
+ signs: {
648
+ slack_post_message: 'text',
649
+ slack_reply_to_thread: 'text',
650
+ },
630
651
  },
631
652
  tavily: {
632
653
  checked: '2026-10-02',
@@ -639,6 +660,7 @@ export const SERVER_ACTIONS = {
639
660
  tavily_research: ['read'],
640
661
  },
641
662
  conditions: {},
663
+ signs: {},
642
664
  },
643
665
  };
644
666
  /**