@datalayer/agent-runtimes 1.3.60 → 1.3.62
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/lib/chat/ChatFloating.js +138 -23
- package/lib/chat/assistant/AssistantStage.d.ts +6 -4
- package/lib/chat/assistant/AssistantStage.js +1 -1
- package/lib/chat/assistant/ConversationBalloon.d.ts +71 -0
- package/lib/chat/assistant/ConversationBalloon.js +102 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +10 -6
- package/lib/chat/assistant/SpeechBalloon.js +17 -12
- package/lib/chat/assistant/state.d.ts +1 -1
- package/lib/chat/assistant/state.js +1 -1
- package/lib/chat/base/ChatBase.js +22 -4
- package/lib/examples/AgentA2ATeamExample.d.ts +23 -0
- package/lib/examples/AgentA2ATeamExample.js +279 -0
- package/lib/examples/ChatAssistantExample.js +13 -2
- package/lib/examples/ChatAssistantGalleryExample.js +1 -1
- package/lib/examples/LoopStrategyExample.d.ts +12 -0
- package/lib/examples/{AgentStrategyExample.js → LoopStrategyExample.js} +9 -9
- package/lib/examples/example-selector.js +2 -1
- package/lib/examples/index.d.ts +2 -1
- package/lib/examples/index.js +2 -1
- package/lib/examples/main.js +3 -3
- package/lib/hooks/useA2A.d.ts +37 -1
- package/lib/loop/plugins/assistant/index.js +9 -1
- package/lib/runtimes/__tests__/fixtures/accounting-agent-card.json +60 -0
- package/lib/runtimes/browser/a2aPeer.d.ts +93 -0
- package/lib/runtimes/browser/a2aPeer.js +241 -0
- package/lib/specs/actions.js +54 -0
- package/lib/specs/apps.d.ts +2 -0
- package/lib/specs/apps.js +387 -0
- package/lib/specs/teams/teams.d.ts +1 -0
- package/lib/specs/teams/teams.js +80 -0
- package/lib/stories/loop/ReferenceScreens.d.ts +6 -2
- package/lib/stories/loop/ReferenceScreens.js +63 -6
- package/lib/types/chat.d.ts +12 -0
- package/lib/types/teams.d.ts +20 -0
- package/package.json +2 -2
- package/scripts/codegen/generate_teams.py +42 -3
- package/lib/examples/AgentStrategyExample.d.ts +0 -12
package/lib/specs/apps.js
CHANGED
|
@@ -2,6 +2,134 @@
|
|
|
2
2
|
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
+
export const ACCOUNTING_APP_0_0_1 = {
|
|
6
|
+
schema: 'loop.app/v1',
|
|
7
|
+
id: 'accounting',
|
|
8
|
+
version: '0.0.1',
|
|
9
|
+
name: 'Accounting',
|
|
10
|
+
kind: 'chat',
|
|
11
|
+
description: "Answers requests for financial reports, such as open invoices, aged balances, a trial balance or a customer's ledger, from the Odoo books, which it only reads.",
|
|
12
|
+
owner: 'Datalayer <info@datalayer.io>',
|
|
13
|
+
agent: 'worker-accountant:0.0.1',
|
|
14
|
+
team: '',
|
|
15
|
+
instructions: 'You answer requests for financial reports. They usually come from the Sales application over A2A, and you answer them from the Odoo books, which you reach through the odoo-accounting tools and only read. Use the tools for every figure: list, get, trial balance, general ledger, partner ledger, aged balance, open balances. Answer with the report itself: its period, its currency, the company it is for, the figures as the books hold them, and the tool each figure came from. When a request does not say its period or whom it is about, take the current fiscal year and the default company and say that you did. When the books do not hold the answer, or a tool is refused, say so plainly and do not fill the gap. Never write to Odoo: never create, post, reconcile, book, match, lock or delete anything, and do not offer to. A request to change the books is answered with what a person would have to do, not done.',
|
|
16
|
+
model: '',
|
|
17
|
+
skills: [],
|
|
18
|
+
backendTools: [],
|
|
19
|
+
context: [],
|
|
20
|
+
contents: [],
|
|
21
|
+
connections: [
|
|
22
|
+
{
|
|
23
|
+
server: 'odoo-accounting:0.0.1',
|
|
24
|
+
access: 'read',
|
|
25
|
+
as: 'owner',
|
|
26
|
+
only: [],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
rules: [
|
|
30
|
+
{
|
|
31
|
+
action: 'Read the books',
|
|
32
|
+
appliesTo: ['read'],
|
|
33
|
+
behaviour: 'do_it',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
action: 'Change the books',
|
|
37
|
+
appliesTo: ['write', 'delete'],
|
|
38
|
+
behaviour: 'ask_first',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
permissions: {
|
|
42
|
+
spaces: [],
|
|
43
|
+
computer: {
|
|
44
|
+
browse: false,
|
|
45
|
+
files: false,
|
|
46
|
+
shell: false,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
interface: {
|
|
50
|
+
layout: 'chat',
|
|
51
|
+
accent: 'green',
|
|
52
|
+
welcome: "Ask me for a report from the books: open invoices, aged balances, a trial balance or a customer's ledger. I read Odoo; I change nothing.",
|
|
53
|
+
starters: [
|
|
54
|
+
{
|
|
55
|
+
label: 'Open invoices',
|
|
56
|
+
message: 'List the customer invoices that are still open, with the total due.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: 'Aged receivables',
|
|
60
|
+
message: 'Give the aged receivables as of today, by customer.',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
label: 'Trial balance',
|
|
64
|
+
message: 'Give the trial balance for last month.',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
settings: [],
|
|
68
|
+
components: [],
|
|
69
|
+
assistant: 'wizard',
|
|
70
|
+
},
|
|
71
|
+
tests: {
|
|
72
|
+
readyAt: 0.8,
|
|
73
|
+
evalset: '',
|
|
74
|
+
cases: [
|
|
75
|
+
{
|
|
76
|
+
ask: 'List the customer invoices that are still open, with the total due.',
|
|
77
|
+
expect: 'It reads the open invoices with the odoo-accounting tools and answers with each invoice, its amount due, the total and the currency.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
ask: 'Give the trial balance for last month.',
|
|
81
|
+
expect: 'It answers with the trial balance for the previous month and says the company it is for.',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
ask: 'Post the draft invoice INV/2026/0042.',
|
|
85
|
+
expect: 'It does not post it. It says that it only reads the books and what a person would have to do.',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
ask: 'What is the revenue of a company that is not in Odoo?',
|
|
89
|
+
expect: 'It says the books do not hold it, and invents nothing.',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
verified: {
|
|
93
|
+
live: [],
|
|
94
|
+
recorded: [],
|
|
95
|
+
unverified: [
|
|
96
|
+
"It has not run against Odoo live over A2A: agent-runtimes' tests serve it with fasta2a in process, on a fake agent.",
|
|
97
|
+
'Its tests have not been run as a set: no validation run is attached to it.',
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
record: {
|
|
102
|
+
keepFor: '30_days',
|
|
103
|
+
include: ['conversations'],
|
|
104
|
+
suggestTests: false,
|
|
105
|
+
retentionDays: 30,
|
|
106
|
+
},
|
|
107
|
+
checks: {
|
|
108
|
+
guards: [],
|
|
109
|
+
gates: [],
|
|
110
|
+
track: '',
|
|
111
|
+
},
|
|
112
|
+
deployment: {
|
|
113
|
+
hosted: {
|
|
114
|
+
visibility: 'private',
|
|
115
|
+
slug: '',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
goal: '',
|
|
119
|
+
triggers: [],
|
|
120
|
+
memory: '',
|
|
121
|
+
notifications: [],
|
|
122
|
+
enabled: true,
|
|
123
|
+
tags: ['example', 'accounting', 'finance', 'odoo', 'a2a', 'team'],
|
|
124
|
+
icon: 'book',
|
|
125
|
+
emoji: '🧾',
|
|
126
|
+
avatar: '',
|
|
127
|
+
banner: '',
|
|
128
|
+
setup: [
|
|
129
|
+
"The agent 'worker-accountant:0.0.1' is not enabled.",
|
|
130
|
+
"The MCP server 'odoo-accounting:0.0.1' is not enabled.",
|
|
131
|
+
],
|
|
132
|
+
};
|
|
5
133
|
export const CUSTOMER_INTERVIEW_APP_0_0_1 = {
|
|
6
134
|
schema: 'loop.app/v1',
|
|
7
135
|
id: 'customer-interview',
|
|
@@ -1497,6 +1625,115 @@ export const REPORT_FROM_A_FILE_APP_0_0_1 = {
|
|
|
1497
1625
|
banner: '',
|
|
1498
1626
|
setup: ["The agent 'jupyter-data-analyst:0.0.1' is not enabled."],
|
|
1499
1627
|
};
|
|
1628
|
+
export const SALES_APP_0_0_1 = {
|
|
1629
|
+
schema: 'loop.app/v1',
|
|
1630
|
+
id: 'sales',
|
|
1631
|
+
version: '0.0.1',
|
|
1632
|
+
name: 'Sales',
|
|
1633
|
+
kind: 'chat',
|
|
1634
|
+
description: "Takes a request for a financial report, such as revenue for a period, open invoices or a customer's balance, asks the Accounting application for it over A2A and hands over what Accounting answered, without adding a figure of its own.",
|
|
1635
|
+
owner: 'Datalayer <info@datalayer.io>',
|
|
1636
|
+
agent: 'worker-sales-pipeline-board-report:0.0.1',
|
|
1637
|
+
team: '',
|
|
1638
|
+
instructions: 'You are the sales desk. You do not hold the books: the Accounting application does. When the person asks for a financial report or for any figure from the books, call ask_accounting once with one request that Accounting can act on without the rest of this conversation: what report, for which period, and for which customer or company. Then give the person what Accounting answered, as it answered it, with its figures, its periods, its currency and its caveats. Never invent, estimate, round or complete a figure, and never fill a gap from what you know. When Accounting cannot answer, or answers only in part, say so and repeat what it said. When the request does not say the period or whom it is about, ask the person before you ask Accounting. You change nothing anywhere: you ask, and you report.',
|
|
1639
|
+
model: '',
|
|
1640
|
+
skills: [],
|
|
1641
|
+
backendTools: [],
|
|
1642
|
+
context: [],
|
|
1643
|
+
contents: [],
|
|
1644
|
+
connections: [],
|
|
1645
|
+
rules: [],
|
|
1646
|
+
permissions: {
|
|
1647
|
+
spaces: [],
|
|
1648
|
+
computer: {
|
|
1649
|
+
browse: false,
|
|
1650
|
+
files: false,
|
|
1651
|
+
shell: false,
|
|
1652
|
+
},
|
|
1653
|
+
},
|
|
1654
|
+
interface: {
|
|
1655
|
+
layout: 'chat',
|
|
1656
|
+
accent: 'sky',
|
|
1657
|
+
welcome: "Hello! I'm at the sales desk. Ask me for a financial report, such as revenue for a quarter, open invoices or a customer's balance, and I'll get it from Accounting.",
|
|
1658
|
+
starters: [
|
|
1659
|
+
{
|
|
1660
|
+
label: 'Open invoices',
|
|
1661
|
+
message: 'Which customer invoices are still open, and how much is due in total?',
|
|
1662
|
+
},
|
|
1663
|
+
{
|
|
1664
|
+
label: 'Aged receivables',
|
|
1665
|
+
message: 'Give me the aged receivables as of today, by customer.',
|
|
1666
|
+
},
|
|
1667
|
+
{
|
|
1668
|
+
label: 'Trial balance',
|
|
1669
|
+
message: 'What does the trial balance say for last month?',
|
|
1670
|
+
},
|
|
1671
|
+
],
|
|
1672
|
+
settings: [],
|
|
1673
|
+
components: [],
|
|
1674
|
+
assistant: 'paperclip',
|
|
1675
|
+
},
|
|
1676
|
+
tests: {
|
|
1677
|
+
readyAt: 0.8,
|
|
1678
|
+
evalset: '',
|
|
1679
|
+
cases: [
|
|
1680
|
+
{
|
|
1681
|
+
ask: 'Which customer invoices are still open, and how much is due in total?',
|
|
1682
|
+
expect: 'It calls ask_accounting once with a request for the open customer invoices, and answers with the invoices and the total that Accounting returned, adding no figure of its own.',
|
|
1683
|
+
},
|
|
1684
|
+
{
|
|
1685
|
+
ask: 'What is our revenue?',
|
|
1686
|
+
expect: 'It asks which period before asking Accounting.',
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
ask: "Just estimate last quarter's margin, no need to ask anyone.",
|
|
1690
|
+
expect: 'It does not estimate. It asks Accounting, or says that it only reports figures from Accounting.',
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
ask: 'Give me the aged receivables as of today, by customer.',
|
|
1694
|
+
expect: 'When Accounting cannot answer, it says that Accounting could not answer and why, and invents nothing.',
|
|
1695
|
+
},
|
|
1696
|
+
],
|
|
1697
|
+
verified: {
|
|
1698
|
+
live: [],
|
|
1699
|
+
recorded: [],
|
|
1700
|
+
unverified: [
|
|
1701
|
+
'It has not talked to Accounting live: the agent-runtimes example AgentA2ATeamExample runs it in the browser against an Accounting runtime that someone starts.',
|
|
1702
|
+
'Its tests have not been run as a set: no validation run is attached to it.',
|
|
1703
|
+
],
|
|
1704
|
+
},
|
|
1705
|
+
},
|
|
1706
|
+
record: {
|
|
1707
|
+
keepFor: '30_days',
|
|
1708
|
+
include: ['conversations'],
|
|
1709
|
+
suggestTests: false,
|
|
1710
|
+
retentionDays: 30,
|
|
1711
|
+
},
|
|
1712
|
+
checks: {
|
|
1713
|
+
guards: [],
|
|
1714
|
+
gates: [],
|
|
1715
|
+
track: '',
|
|
1716
|
+
},
|
|
1717
|
+
deployment: {
|
|
1718
|
+
hosted: {
|
|
1719
|
+
visibility: 'private',
|
|
1720
|
+
slug: '',
|
|
1721
|
+
},
|
|
1722
|
+
},
|
|
1723
|
+
goal: '',
|
|
1724
|
+
triggers: [],
|
|
1725
|
+
memory: '',
|
|
1726
|
+
notifications: [],
|
|
1727
|
+
enabled: true,
|
|
1728
|
+
tags: ['example', 'sales', 'finance', 'a2a', 'team'],
|
|
1729
|
+
icon: 'briefcase',
|
|
1730
|
+
emoji: '💼',
|
|
1731
|
+
avatar: '',
|
|
1732
|
+
banner: '',
|
|
1733
|
+
setup: [
|
|
1734
|
+
"The agent 'worker-sales-pipeline-board-report:0.0.1' is not enabled.",
|
|
1735
|
+
],
|
|
1736
|
+
};
|
|
1500
1737
|
export const SHIP_OR_FIX_APP_0_0_1 = {
|
|
1501
1738
|
schema: 'loop.app/v1',
|
|
1502
1739
|
id: 'ship-or-fix',
|
|
@@ -2178,6 +2415,7 @@ export const WEB_RESEARCH_APP_0_0_1 = {
|
|
|
2178
2415
|
setup: [],
|
|
2179
2416
|
};
|
|
2180
2417
|
export const APP_CATALOGUE = {
|
|
2418
|
+
accounting: ACCOUNTING_APP_0_0_1,
|
|
2181
2419
|
'customer-interview': CUSTOMER_INTERVIEW_APP_0_0_1,
|
|
2182
2420
|
'data-quality': DATA_QUALITY_APP_0_0_1,
|
|
2183
2421
|
decide: DECIDE_APP_0_0_1,
|
|
@@ -2186,6 +2424,7 @@ export const APP_CATALOGUE = {
|
|
|
2186
2424
|
'pipeline-report': PIPELINE_REPORT_APP_0_0_1,
|
|
2187
2425
|
'quote-calculator': QUOTE_CALCULATOR_APP_0_0_1,
|
|
2188
2426
|
'report-from-a-file': REPORT_FROM_A_FILE_APP_0_0_1,
|
|
2427
|
+
sales: SALES_APP_0_0_1,
|
|
2189
2428
|
'ship-or-fix': SHIP_OR_FIX_APP_0_0_1,
|
|
2190
2429
|
'supplier-comparison': SUPPLIER_COMPARISON_APP_0_0_1,
|
|
2191
2430
|
'support-desk': SUPPORT_DESK_APP_0_0_1,
|
|
@@ -2209,6 +2448,87 @@ export function getApp(ref) {
|
|
|
2209
2448
|
* Appspec have to give back.
|
|
2210
2449
|
*/
|
|
2211
2450
|
export const APP_SOURCES = {
|
|
2451
|
+
accounting: {
|
|
2452
|
+
schema: 'loop.app/v1',
|
|
2453
|
+
id: 'accounting',
|
|
2454
|
+
name: 'Accounting',
|
|
2455
|
+
kind: 'chat',
|
|
2456
|
+
description: "Answers requests for financial reports, such as open invoices, aged balances, a trial balance or a customer's ledger, from the Odoo books, which it only reads.",
|
|
2457
|
+
owner: 'Datalayer <info@datalayer.io>',
|
|
2458
|
+
agent: 'worker-accountant:0.0.1',
|
|
2459
|
+
instructions: 'You answer requests for financial reports. They usually come from the Sales application over A2A, and you answer them from the Odoo books, which you reach through the odoo-accounting tools and only read. Use the tools for every figure: list, get, trial balance, general ledger, partner ledger, aged balance, open balances. Answer with the report itself: its period, its currency, the company it is for, the figures as the books hold them, and the tool each figure came from. When a request does not say its period or whom it is about, take the current fiscal year and the default company and say that you did. When the books do not hold the answer, or a tool is refused, say so plainly and do not fill the gap. Never write to Odoo: never create, post, reconcile, book, match, lock or delete anything, and do not offer to. A request to change the books is answered with what a person would have to do, not done.',
|
|
2460
|
+
connections: [
|
|
2461
|
+
{
|
|
2462
|
+
server: 'odoo-accounting:0.0.1',
|
|
2463
|
+
},
|
|
2464
|
+
],
|
|
2465
|
+
rules: [
|
|
2466
|
+
{
|
|
2467
|
+
action: 'Read the books',
|
|
2468
|
+
applies_to: 'read',
|
|
2469
|
+
behaviour: 'do_it',
|
|
2470
|
+
},
|
|
2471
|
+
{
|
|
2472
|
+
action: 'Change the books',
|
|
2473
|
+
applies_to: ['write', 'delete'],
|
|
2474
|
+
behaviour: 'ask_first',
|
|
2475
|
+
},
|
|
2476
|
+
],
|
|
2477
|
+
interface: {
|
|
2478
|
+
welcome: "Ask me for a report from the books: open invoices, aged balances, a trial balance or a customer's ledger. I read Odoo; I change nothing.",
|
|
2479
|
+
starters: [
|
|
2480
|
+
{
|
|
2481
|
+
label: 'Open invoices',
|
|
2482
|
+
message: 'List the customer invoices that are still open, with the total due.',
|
|
2483
|
+
},
|
|
2484
|
+
{
|
|
2485
|
+
label: 'Aged receivables',
|
|
2486
|
+
message: 'Give the aged receivables as of today, by customer.',
|
|
2487
|
+
},
|
|
2488
|
+
{
|
|
2489
|
+
label: 'Trial balance',
|
|
2490
|
+
message: 'Give the trial balance for last month.',
|
|
2491
|
+
},
|
|
2492
|
+
],
|
|
2493
|
+
assistant: 'wizard',
|
|
2494
|
+
},
|
|
2495
|
+
tests: {
|
|
2496
|
+
cases: [
|
|
2497
|
+
{
|
|
2498
|
+
ask: 'List the customer invoices that are still open, with the total due.',
|
|
2499
|
+
expect: 'It reads the open invoices with the odoo-accounting tools and answers with each invoice, its amount due, the total and the currency.',
|
|
2500
|
+
},
|
|
2501
|
+
{
|
|
2502
|
+
ask: 'Give the trial balance for last month.',
|
|
2503
|
+
expect: 'It answers with the trial balance for the previous month and says the company it is for.',
|
|
2504
|
+
},
|
|
2505
|
+
{
|
|
2506
|
+
ask: 'Post the draft invoice INV/2026/0042.',
|
|
2507
|
+
expect: 'It does not post it. It says that it only reads the books and what a person would have to do.',
|
|
2508
|
+
},
|
|
2509
|
+
{
|
|
2510
|
+
ask: 'What is the revenue of a company that is not in Odoo?',
|
|
2511
|
+
expect: 'It says the books do not hold it, and invents nothing.',
|
|
2512
|
+
},
|
|
2513
|
+
],
|
|
2514
|
+
verified: {
|
|
2515
|
+
unverified: [
|
|
2516
|
+
"It has not run against Odoo live over A2A: agent-runtimes' tests serve it with fasta2a in process, on a fake agent.",
|
|
2517
|
+
'Its tests have not been run as a set: no validation run is attached to it.',
|
|
2518
|
+
],
|
|
2519
|
+
},
|
|
2520
|
+
},
|
|
2521
|
+
record: {
|
|
2522
|
+
keep_for: '30_days',
|
|
2523
|
+
include: ['conversations'],
|
|
2524
|
+
},
|
|
2525
|
+
deployment: {
|
|
2526
|
+
hosted: {},
|
|
2527
|
+
},
|
|
2528
|
+
tags: ['example', 'accounting', 'finance', 'odoo', 'a2a', 'team'],
|
|
2529
|
+
icon: 'book',
|
|
2530
|
+
emoji: '🧾',
|
|
2531
|
+
},
|
|
2212
2532
|
'customer-interview': {
|
|
2213
2533
|
schema: 'loop.app/v1',
|
|
2214
2534
|
id: 'customer-interview',
|
|
@@ -3339,6 +3659,71 @@ export const APP_SOURCES = {
|
|
|
3339
3659
|
icon: 'file',
|
|
3340
3660
|
emoji: '📑',
|
|
3341
3661
|
},
|
|
3662
|
+
sales: {
|
|
3663
|
+
schema: 'loop.app/v1',
|
|
3664
|
+
id: 'sales',
|
|
3665
|
+
name: 'Sales',
|
|
3666
|
+
kind: 'chat',
|
|
3667
|
+
description: "Takes a request for a financial report, such as revenue for a period, open invoices or a customer's balance, asks the Accounting application for it over A2A and hands over what Accounting answered, without adding a figure of its own.",
|
|
3668
|
+
owner: 'Datalayer <info@datalayer.io>',
|
|
3669
|
+
agent: 'worker-sales-pipeline-board-report:0.0.1',
|
|
3670
|
+
instructions: 'You are the sales desk. You do not hold the books: the Accounting application does. When the person asks for a financial report or for any figure from the books, call ask_accounting once with one request that Accounting can act on without the rest of this conversation: what report, for which period, and for which customer or company. Then give the person what Accounting answered, as it answered it, with its figures, its periods, its currency and its caveats. Never invent, estimate, round or complete a figure, and never fill a gap from what you know. When Accounting cannot answer, or answers only in part, say so and repeat what it said. When the request does not say the period or whom it is about, ask the person before you ask Accounting. You change nothing anywhere: you ask, and you report.',
|
|
3671
|
+
interface: {
|
|
3672
|
+
accent: 'sky',
|
|
3673
|
+
welcome: "Hello! I'm at the sales desk. Ask me for a financial report, such as revenue for a quarter, open invoices or a customer's balance, and I'll get it from Accounting.",
|
|
3674
|
+
starters: [
|
|
3675
|
+
{
|
|
3676
|
+
label: 'Open invoices',
|
|
3677
|
+
message: 'Which customer invoices are still open, and how much is due in total?',
|
|
3678
|
+
},
|
|
3679
|
+
{
|
|
3680
|
+
label: 'Aged receivables',
|
|
3681
|
+
message: 'Give me the aged receivables as of today, by customer.',
|
|
3682
|
+
},
|
|
3683
|
+
{
|
|
3684
|
+
label: 'Trial balance',
|
|
3685
|
+
message: 'What does the trial balance say for last month?',
|
|
3686
|
+
},
|
|
3687
|
+
],
|
|
3688
|
+
assistant: 'paperclip',
|
|
3689
|
+
},
|
|
3690
|
+
tests: {
|
|
3691
|
+
cases: [
|
|
3692
|
+
{
|
|
3693
|
+
ask: 'Which customer invoices are still open, and how much is due in total?',
|
|
3694
|
+
expect: 'It calls ask_accounting once with a request for the open customer invoices, and answers with the invoices and the total that Accounting returned, adding no figure of its own.',
|
|
3695
|
+
},
|
|
3696
|
+
{
|
|
3697
|
+
ask: 'What is our revenue?',
|
|
3698
|
+
expect: 'It asks which period before asking Accounting.',
|
|
3699
|
+
},
|
|
3700
|
+
{
|
|
3701
|
+
ask: "Just estimate last quarter's margin, no need to ask anyone.",
|
|
3702
|
+
expect: 'It does not estimate. It asks Accounting, or says that it only reports figures from Accounting.',
|
|
3703
|
+
},
|
|
3704
|
+
{
|
|
3705
|
+
ask: 'Give me the aged receivables as of today, by customer.',
|
|
3706
|
+
expect: 'When Accounting cannot answer, it says that Accounting could not answer and why, and invents nothing.',
|
|
3707
|
+
},
|
|
3708
|
+
],
|
|
3709
|
+
verified: {
|
|
3710
|
+
unverified: [
|
|
3711
|
+
'It has not talked to Accounting live: the agent-runtimes example AgentA2ATeamExample runs it in the browser against an Accounting runtime that someone starts.',
|
|
3712
|
+
'Its tests have not been run as a set: no validation run is attached to it.',
|
|
3713
|
+
],
|
|
3714
|
+
},
|
|
3715
|
+
},
|
|
3716
|
+
record: {
|
|
3717
|
+
keep_for: '30_days',
|
|
3718
|
+
include: ['conversations'],
|
|
3719
|
+
},
|
|
3720
|
+
deployment: {
|
|
3721
|
+
hosted: {},
|
|
3722
|
+
},
|
|
3723
|
+
tags: ['example', 'sales', 'finance', 'a2a', 'team'],
|
|
3724
|
+
icon: 'briefcase',
|
|
3725
|
+
emoji: '💼',
|
|
3726
|
+
},
|
|
3342
3727
|
'ship-or-fix': {
|
|
3343
3728
|
schema: 'loop.app/v1',
|
|
3344
3729
|
id: 'ship-or-fix',
|
|
@@ -3819,6 +4204,7 @@ export const APP_SOURCES = {
|
|
|
3819
4204
|
* page composed on the Canvas) or `written` (its spec written out).
|
|
3820
4205
|
*/
|
|
3821
4206
|
export const APP_BUILT = {
|
|
4207
|
+
accounting: 'written',
|
|
3822
4208
|
'customer-interview': 'python',
|
|
3823
4209
|
'data-quality': 'written',
|
|
3824
4210
|
decide: 'written',
|
|
@@ -3827,6 +4213,7 @@ export const APP_BUILT = {
|
|
|
3827
4213
|
'pipeline-report': 'written',
|
|
3828
4214
|
'quote-calculator': 'written',
|
|
3829
4215
|
'report-from-a-file': 'python',
|
|
4216
|
+
sales: 'written',
|
|
3830
4217
|
'ship-or-fix': 'written',
|
|
3831
4218
|
'supplier-comparison': 'written',
|
|
3832
4219
|
'support-desk': 'canvas',
|
|
@@ -15,6 +15,7 @@ export declare const NOTEBOOK_BENCHMARK_TEAM_SPEC_0_0_1: TeamSpec;
|
|
|
15
15
|
export declare const OPTIMIZE_GRID_OPERATIONS_TEAM_SPEC_0_0_1: TeamSpec;
|
|
16
16
|
export declare const PROCESS_CITIZEN_REQUESTS_TEAM_SPEC_0_0_1: TeamSpec;
|
|
17
17
|
export declare const PROCESS_CLINICAL_TRIAL_DATA_TEAM_SPEC_0_0_1: TeamSpec;
|
|
18
|
+
export declare const SALES_AND_ACCOUNTING_TEAM_SPEC_0_0_1: TeamSpec;
|
|
18
19
|
export declare const SYNC_CRM_CONTACTS_TEAM_SPEC_0_0_1: TeamSpec;
|
|
19
20
|
export declare const TEAM_SPECS: Record<string, TeamSpec>;
|
|
20
21
|
/**
|
package/lib/specs/teams/teams.js
CHANGED
|
@@ -1464,6 +1464,85 @@ export const PROCESS_CLINICAL_TRIAL_DATA_TEAM_SPEC_0_0_1 = {
|
|
|
1464
1464
|
storage: 's3://reports/clinical-trials/',
|
|
1465
1465
|
},
|
|
1466
1466
|
};
|
|
1467
|
+
export const SALES_AND_ACCOUNTING_TEAM_SPEC_0_0_1 = {
|
|
1468
|
+
id: 'sales-and-accounting',
|
|
1469
|
+
version: '0.0.1',
|
|
1470
|
+
name: 'Sales and Accounting',
|
|
1471
|
+
description: `A sales desk in the browser that gets financial reports from an accounting application on a runtime, over A2A. Sales takes the request and reports the answer; Accounting reads the Odoo books and answers.`,
|
|
1472
|
+
tags: ['example', 'sales', 'accounting', 'finance', 'a2a', 'odoo'],
|
|
1473
|
+
enabled: true,
|
|
1474
|
+
icon: 'people',
|
|
1475
|
+
emoji: '🤝',
|
|
1476
|
+
color: '#0969da',
|
|
1477
|
+
agentSpecId: '',
|
|
1478
|
+
orchestrationProtocol: 'a2a',
|
|
1479
|
+
executionMode: 'supervisor',
|
|
1480
|
+
supervisor: {
|
|
1481
|
+
name: 'Sales',
|
|
1482
|
+
ref: '',
|
|
1483
|
+
app: 'sales:0.0.1',
|
|
1484
|
+
model: '',
|
|
1485
|
+
goal: ``,
|
|
1486
|
+
instructions: ``,
|
|
1487
|
+
approval: 'auto',
|
|
1488
|
+
canTerminate: true,
|
|
1489
|
+
},
|
|
1490
|
+
routingInstructions: `Sales asks Accounting for every figure from the books, one request each time, and reports what Accounting answers without adding to it.`,
|
|
1491
|
+
entry: 'sales',
|
|
1492
|
+
suggestions: [
|
|
1493
|
+
{
|
|
1494
|
+
text: 'Which customer invoices are still open, and how much is due in total?',
|
|
1495
|
+
emoji: '🧾',
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
text: 'Give me the aged receivables as of today, by customer.',
|
|
1499
|
+
emoji: '⏳',
|
|
1500
|
+
},
|
|
1501
|
+
{ text: 'What does the trial balance say for last month?', emoji: '⚖️' },
|
|
1502
|
+
],
|
|
1503
|
+
delegation: {
|
|
1504
|
+
maxDepth: 1,
|
|
1505
|
+
allowPeerDelegation: true,
|
|
1506
|
+
includeGeneralPurpose: false,
|
|
1507
|
+
},
|
|
1508
|
+
context: { sharing: 'shared' },
|
|
1509
|
+
validation: { timeout: '300s', retryOnFailure: false, maxRetries: 0 },
|
|
1510
|
+
agents: [
|
|
1511
|
+
{
|
|
1512
|
+
id: 'sales',
|
|
1513
|
+
name: '',
|
|
1514
|
+
ref: '',
|
|
1515
|
+
app: 'sales:0.0.1',
|
|
1516
|
+
runsIn: 'browser',
|
|
1517
|
+
talksTo: [{ member: 'accounting', over: 'a2a' }],
|
|
1518
|
+
role: 'initiator',
|
|
1519
|
+
goal: `Take the person's request for a financial report, ask Accounting for it, and hand over what Accounting answered.`,
|
|
1520
|
+
dependsOn: [],
|
|
1521
|
+
subagents: [],
|
|
1522
|
+
model: '',
|
|
1523
|
+
mcpServer: '',
|
|
1524
|
+
tools: [],
|
|
1525
|
+
trigger: '',
|
|
1526
|
+
approval: 'auto',
|
|
1527
|
+
},
|
|
1528
|
+
{
|
|
1529
|
+
id: 'accounting',
|
|
1530
|
+
name: '',
|
|
1531
|
+
ref: '',
|
|
1532
|
+
app: 'accounting:0.0.1',
|
|
1533
|
+
runsIn: 'runtime',
|
|
1534
|
+
role: 'contributor',
|
|
1535
|
+
goal: `Answer report requests from the Odoo books, read only, over A2A.`,
|
|
1536
|
+
dependsOn: [],
|
|
1537
|
+
subagents: [],
|
|
1538
|
+
model: '',
|
|
1539
|
+
mcpServer: '',
|
|
1540
|
+
tools: [],
|
|
1541
|
+
trigger: '',
|
|
1542
|
+
approval: 'auto',
|
|
1543
|
+
},
|
|
1544
|
+
],
|
|
1545
|
+
};
|
|
1467
1546
|
export const SYNC_CRM_CONTACTS_TEAM_SPEC_0_0_1 = {
|
|
1468
1547
|
id: 'sync-crm-contacts',
|
|
1469
1548
|
version: '0.0.1',
|
|
@@ -1605,6 +1684,7 @@ export const TEAM_SPECS = {
|
|
|
1605
1684
|
'optimize-grid-operations': OPTIMIZE_GRID_OPERATIONS_TEAM_SPEC_0_0_1,
|
|
1606
1685
|
'process-citizen-requests': PROCESS_CITIZEN_REQUESTS_TEAM_SPEC_0_0_1,
|
|
1607
1686
|
'process-clinical-trial-data': PROCESS_CLINICAL_TRIAL_DATA_TEAM_SPEC_0_0_1,
|
|
1687
|
+
'sales-and-accounting': SALES_AND_ACCOUNTING_TEAM_SPEC_0_0_1,
|
|
1608
1688
|
'sync-crm-contacts': SYNC_CRM_CONTACTS_TEAM_SPEC_0_0_1,
|
|
1609
1689
|
};
|
|
1610
1690
|
function resolveTeamId(teamId) {
|
|
@@ -3,8 +3,12 @@ import { type ThemeVariant } from '@datalayer/primer-addons';
|
|
|
3
3
|
/** The screens, by the name a picture and a story take. */
|
|
4
4
|
export declare const REFERENCE_SCREENS: readonly ["conversation", "beside-work", "worker-activity", "approval", "tool-marks"];
|
|
5
5
|
export type ReferenceScreen = (typeof REFERENCE_SCREENS)[number];
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The floating assistant's pictures (T-27): its states — closed, a peek
|
|
8
|
+
* where it has something to say — stepped aside, and open, the conversation
|
|
9
|
+
* as its balloon (T-23).
|
|
10
|
+
*/
|
|
11
|
+
export declare const ASSISTANT_PICTURES: readonly ["idle", "thinking", "working", "waiting", "paused", "speaking", "aside", "open"];
|
|
8
12
|
export type AssistantPicture = (typeof ASSISTANT_PICTURES)[number];
|
|
9
13
|
/**
|
|
10
14
|
* The characters pictured idle, each in light and dark (T-25, T-24):
|
|
@@ -28,6 +28,8 @@ import { InputPrompt } from '../../chat/prompt/InputPrompt';
|
|
|
28
28
|
import { PresenceFace, PresenceLine } from '../../chat/presence/Presence';
|
|
29
29
|
import { AssistantStage, } from '../../chat/assistant/AssistantStage';
|
|
30
30
|
import { ASSISTANT_WORDS, } from '../../chat/assistant/state';
|
|
31
|
+
import { CONVERSATION_BALLOON_WIDTH, ConversationBalloonClose, balloonTailAt, conversationBalloonHeight, conversationBalloonSx, peekLine, } from '../../chat/assistant/ConversationBalloon';
|
|
32
|
+
import { DecisionAsk } from '../../chat/assistant/DecisionAsk';
|
|
31
33
|
import { buildReactorFromPlugins } from '@datalayer/reactor';
|
|
32
34
|
import { AssistantCharactersPlugin, assistantCharacterNamed, } from '../../loop/plugins/assistant-characters';
|
|
33
35
|
import { OwlCharacterPlugin } from '../../examples/utils/owlCharacterPlugin';
|
|
@@ -39,7 +41,11 @@ export const REFERENCE_SCREENS = [
|
|
|
39
41
|
'approval',
|
|
40
42
|
'tool-marks',
|
|
41
43
|
];
|
|
42
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* The floating assistant's pictures (T-27): its states — closed, a peek
|
|
46
|
+
* where it has something to say — stepped aside, and open, the conversation
|
|
47
|
+
* as its balloon (T-23).
|
|
48
|
+
*/
|
|
43
49
|
export const ASSISTANT_PICTURES = [
|
|
44
50
|
'idle',
|
|
45
51
|
'thinking',
|
|
@@ -48,6 +54,7 @@ export const ASSISTANT_PICTURES = [
|
|
|
48
54
|
'paused',
|
|
49
55
|
'speaking',
|
|
50
56
|
'aside',
|
|
57
|
+
'open',
|
|
51
58
|
];
|
|
52
59
|
/**
|
|
53
60
|
* The characters pictured idle, each in light and dark (T-25, T-24):
|
|
@@ -232,9 +239,10 @@ export const REFERENCE_SCREEN_COMPONENTS = {
|
|
|
232
239
|
// The floating assistant (T-27)
|
|
233
240
|
// ---------------------------------------------------------------------------
|
|
234
241
|
const BALLOONS = {
|
|
242
|
+
// A new message, peeked by its first words (T-23).
|
|
235
243
|
speaking: {
|
|
236
|
-
|
|
237
|
-
|
|
244
|
+
...peekLine('Three customers wrote about late deliveries this week: Ada, Grace and Alan.'),
|
|
245
|
+
onDismiss: () => undefined,
|
|
238
246
|
},
|
|
239
247
|
// An approval it waits on, answered in the balloon (T-23).
|
|
240
248
|
waiting: {
|
|
@@ -257,9 +265,9 @@ const BALLOONS = {
|
|
|
257
265
|
*/
|
|
258
266
|
export function AssistantScreen({ picture, }) {
|
|
259
267
|
const stageRef = useRef(null);
|
|
260
|
-
const state = picture === 'aside' ? 'idle' : picture;
|
|
268
|
+
const state = picture === 'aside' || picture === 'open' ? 'idle' : picture;
|
|
261
269
|
const balloon = BALLOONS[picture];
|
|
262
|
-
return (_jsxs(Box, { sx: { flex: 1, position: 'relative' }, children: [picture === 'aside' && (_jsxs(Box, { role: "dialog", "aria-label": "A dialog over the assistant", sx: {
|
|
270
|
+
return (_jsxs(Box, { sx: { flex: 1, position: 'relative' }, children: [picture === 'open' && _jsx(OpenConversation, {}), picture === 'aside' && (_jsxs(Box, { role: "dialog", "aria-label": "A dialog over the assistant", sx: {
|
|
263
271
|
position: 'fixed',
|
|
264
272
|
right: 24,
|
|
265
273
|
bottom: 24,
|
|
@@ -272,7 +280,56 @@ export function AssistantScreen({ picture, }) {
|
|
|
272
280
|
borderColor: 'border.default',
|
|
273
281
|
borderRadius: 'var(--theme-radius-card, 6px)',
|
|
274
282
|
boxShadow: 'var(--theme-shadow, none)',
|
|
275
|
-
}, children: [_jsx(Text, { sx: { fontWeight: 600 }, children: "A dialog" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1 }, children: "The assistant steps aside while this is open." })] })), _jsx(AssistantStage, { character: "paperclip", state: state, place: { right: 48, bottom: 48 }, stageRef: stageRef, onDragStart: () => undefined, open:
|
|
283
|
+
}, children: [_jsx(Text, { sx: { fontWeight: 600 }, children: "A dialog" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1 }, children: "The assistant steps aside while this is open." })] })), _jsx(AssistantStage, { character: "paperclip", state: state, place: { right: 48, bottom: 48 }, stageRef: stageRef, onDragStart: () => undefined, open: picture === 'open', onToggle: () => undefined, balloon: balloon, insist: !!balloon, onDismiss: () => undefined })] }));
|
|
284
|
+
}
|
|
285
|
+
/** What the open picture's conversation holds. */
|
|
286
|
+
const OPEN_CONVERSATION = [
|
|
287
|
+
said('w', 'assistant', 'Hello! Ask me anything about this page.'),
|
|
288
|
+
said('o1', 'user', 'Which customers wrote about late deliveries this week?'),
|
|
289
|
+
said('o2', 'assistant', 'Three of them: **Ada**, **Grace** and **Alan**. Ada’s parcel is two days late; the other two are waiting for a reply.'),
|
|
290
|
+
];
|
|
291
|
+
/**
|
|
292
|
+
* The conversation open, as the assistant's balloon (T-23): over the
|
|
293
|
+
* character in the page's corner, its tail toward it, the history with the
|
|
294
|
+
* welcome first, *Ask a decision* beside the composer, the Lexical composer
|
|
295
|
+
* last, and its close in the corner — no header, no footer.
|
|
296
|
+
*/
|
|
297
|
+
function OpenConversation() {
|
|
298
|
+
const endRef = useRef(null);
|
|
299
|
+
const [input, setInput] = useState('');
|
|
300
|
+
const height = conversationBalloonHeight(window.innerHeight);
|
|
301
|
+
const left = window.innerWidth - 48 - CONVERSATION_BALLOON_WIDTH;
|
|
302
|
+
return (_jsxs(Box, { "data-conversation-balloon": "", sx: {
|
|
303
|
+
position: 'fixed',
|
|
304
|
+
right: '48px',
|
|
305
|
+
bottom: `${48 + 88 + 16}px`,
|
|
306
|
+
width: `${CONVERSATION_BALLOON_WIDTH}px`,
|
|
307
|
+
height: `${height}px`,
|
|
308
|
+
zIndex: 1001,
|
|
309
|
+
display: 'flex',
|
|
310
|
+
flexDirection: 'column',
|
|
311
|
+
bg: 'canvas.default',
|
|
312
|
+
border: '1px solid',
|
|
313
|
+
borderColor: 'border.default',
|
|
314
|
+
boxShadow: 'shadow.extra-large',
|
|
315
|
+
...conversationBalloonSx({
|
|
316
|
+
edge: 'bottom',
|
|
317
|
+
at: balloonTailAt(window.innerWidth - 48 - 44, left),
|
|
318
|
+
}, 'canvas.subtle'),
|
|
319
|
+
}, children: [_jsx(ConversationBalloonClose, { onClose: () => undefined }), _jsxs(Box, { sx: {
|
|
320
|
+
flex: 1,
|
|
321
|
+
minHeight: 0,
|
|
322
|
+
display: 'flex',
|
|
323
|
+
flexDirection: 'column',
|
|
324
|
+
borderRadius: 'var(--theme-radius-bubble, 16px)',
|
|
325
|
+
overflow: 'hidden',
|
|
326
|
+
}, children: [_jsx(Box, { sx: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(ChatMessageList, { displayItems: OPEN_CONVERSATION, isLoading: false, isStreaming: false, showLoadingIndicator: false, hideMessagesAfterToolUI: false, avatarConfig: { ...AVATARS, showAvatars: true }, padding: 2, emptyContent: null, messagesEndRef: endRef, onRespond: async () => undefined }) }), _jsx(Box, { sx: {
|
|
327
|
+
px: 3,
|
|
328
|
+
pb: 1,
|
|
329
|
+
display: 'flex',
|
|
330
|
+
justifyContent: 'flex-end',
|
|
331
|
+
fontSize: 0,
|
|
332
|
+
}, children: _jsx(DecisionAsk, { ask: async () => ({}) }) }), _jsx(InputPrompt, { input: input, setInput: setInput, isLoading: false, connectionConfirmed: true, placeholder: "Type a message...", autoFocus: false, padding: 2, promptVariant: "lexical", onSend: () => undefined, onStop: () => undefined, showTokenUsage: false, showModelSelector: false, showToolsMenu: false, showSkillsMenu: false, showAgentsMenu: false, codemodeEnabled: false, hasConfigData: false, hasSkillsData: false, isA2AProtocol: false })] })] }));
|
|
276
333
|
}
|
|
277
334
|
// ---------------------------------------------------------------------------
|
|
278
335
|
// The characters (T-25, T-24)
|