experimental-a2 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/{actor-DJi3RsNu.d.ts → actor-BfQSE0KC.d.ts} +4 -4
- package/dist/{actor-DJi3RsNu.d.ts.map → actor-BfQSE0KC.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +1 -1
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +2 -2
- package/dist/{actor-shared-DI7J5upy.js → actor-shared-B5tJfzt-.js} +2 -2
- package/dist/{actor-shared-DI7J5upy.js.map → actor-shared-B5tJfzt-.js.map} +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +3 -3
- package/dist/ai-Cai-lCbj.d.ts +580 -0
- package/dist/ai-Cai-lCbj.d.ts.map +1 -0
- package/dist/ai-control-CcD4hh3y.js +119 -0
- package/dist/ai-control-CcD4hh3y.js.map +1 -0
- package/dist/ai-server.d.ts +6 -6
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +1014 -501
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -365
- package/dist/ai.js +801 -80
- package/dist/ai.js.map +1 -1
- package/dist/{client-P_NNNRM-.d.ts → client-BAEABRZB.d.ts} +2 -2
- package/dist/{client-P_NNNRM-.d.ts.map → client-BAEABRZB.d.ts.map} +1 -1
- package/dist/{client-Bf6uSEAk.js → client-BYzHjkwU.js} +21 -6
- package/dist/client-BYzHjkwU.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/{contract-48bUMgcL.js → contract-CKRg_E4q.js} +3 -26
- package/dist/contract-CKRg_E4q.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{reducer-DJKWm3cp.d.ts → reducer-BcS9VDKC.d.ts} +4 -1
- package/dist/{reducer-DJKWm3cp.d.ts.map → reducer-BcS9VDKC.d.ts.map} +1 -1
- package/dist/reducer-DEMjEY_O.js +29 -0
- package/dist/reducer-DEMjEY_O.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +1 -1
- package/dist/{server-DjZZa1wr.d.ts → server-Bp5Nd1pF.d.ts} +3 -3
- package/dist/{server-DjZZa1wr.d.ts.map → server-Bp5Nd1pF.d.ts.map} +1 -1
- package/dist/{server-BeNADlCI.js → server-CjJSGcF7.js} +4 -3
- package/dist/server-CjJSGcF7.js.map +1 -0
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/{store-DtDOWLSn.d.ts → store-D_yhNdPz.d.ts} +7 -2
- package/dist/{store-DtDOWLSn.d.ts.map → store-D_yhNdPz.d.ts.map} +1 -1
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.js +19 -0
- package/dist/store-postgres.js.map +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +1 -1
- package/dist/{store-redis-notify-BUCyXOn0.js → store-redis-notify-D2EI6gwX.js} +27 -2
- package/dist/store-redis-notify-D2EI6gwX.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/docs/guides/06-ai-agents.mdx +265 -62
- package/docs/reference/01-api.mdx +122 -27
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +118 -21
- package/examples/playground/app/agent/[agentId]/agent-queue.tsx +289 -0
- package/examples/playground/app/agent/compaction-settings.test.ts +22 -6
- package/examples/playground/app/agent/model.ts +11 -2
- package/examples/playground/app/agent/server.ts +8 -2
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +3 -13
- package/examples/playground/app/chat/model.ts +2 -2
- package/examples/playground/app/chat/server.ts +24 -17
- package/examples/playground/app/globals.css +179 -0
- package/examples/playground/package.json +1 -1
- package/package.json +1 -1
- package/src/ai-client-state.ts +185 -0
- package/src/ai-control-server.ts +829 -0
- package/src/ai-control-state.ts +152 -0
- package/src/ai-control.ts +139 -0
- package/src/ai-coordinator.ts +99 -32
- package/src/ai-progress-batches.ts +68 -0
- package/src/ai-projector.ts +76 -15
- package/src/ai-sdk-step.ts +0 -1
- package/src/ai-server.ts +381 -608
- package/src/ai.ts +553 -108
- package/src/client.ts +31 -9
- package/src/licenses/Apache-2.0.txt +55 -0
- package/src/parse-partial-json.ts +441 -0
- package/src/reducer.ts +6 -0
- package/src/server.ts +8 -4
- package/src/store-postgres.ts +27 -0
- package/src/store-redis-core.ts +53 -1
- package/src/store-redis-notify.ts +1 -0
- package/src/store.ts +6 -0
- package/dist/ai.d.ts.map +0 -1
- package/dist/client-Bf6uSEAk.js.map +0 -1
- package/dist/contract-48bUMgcL.js.map +0 -1
- package/dist/server-BeNADlCI.js.map +0 -1
- package/dist/store-redis-notify-BUCyXOn0.js.map +0 -1
|
@@ -21,16 +21,13 @@ const tools = {
|
|
|
21
21
|
inputSchema: chatPostInputSchema,
|
|
22
22
|
execute: async ({ message }, { toolCallId }) => {
|
|
23
23
|
const context = handlerContext(chatAgent)
|
|
24
|
-
if (context.event.type !== 'ai.tool.called') {
|
|
25
|
-
throw new Error('The chat post tool must execute automatically')
|
|
26
|
-
}
|
|
27
24
|
context.signal.throwIfAborted()
|
|
28
25
|
await context.session.append('post', {
|
|
29
26
|
type: 'agent.message.posted',
|
|
30
27
|
payload: {
|
|
31
28
|
message,
|
|
32
|
-
generationId: context.event.payload.generationId,
|
|
33
|
-
responseMessageId: context.event.payload.messageId,
|
|
29
|
+
generationId: context.event.payload.call.generationId,
|
|
30
|
+
responseMessageId: context.event.payload.call.messageId,
|
|
34
31
|
toolCallId,
|
|
35
32
|
},
|
|
36
33
|
})
|
|
@@ -118,23 +115,33 @@ export const authorizeChatOperation = (
|
|
|
118
115
|
return false
|
|
119
116
|
}
|
|
120
117
|
return !(
|
|
121
|
-
event.type === 'ai.
|
|
122
|
-
event.
|
|
123
|
-
event.
|
|
124
|
-
event.
|
|
118
|
+
event.type === 'ai.control.requested' &&
|
|
119
|
+
typeof event.payload === 'object' &&
|
|
120
|
+
event.payload !== null &&
|
|
121
|
+
'action' in event.payload &&
|
|
122
|
+
typeof event.payload.action === 'string' &&
|
|
123
|
+
[
|
|
124
|
+
'approval',
|
|
125
|
+
'input',
|
|
126
|
+
'interrupt',
|
|
127
|
+
'stop',
|
|
128
|
+
'retry',
|
|
129
|
+
'pause',
|
|
130
|
+
'resume',
|
|
131
|
+
].includes(event.payload.action)
|
|
125
132
|
)
|
|
126
133
|
})
|
|
127
134
|
return rejected === undefined
|
|
128
135
|
}
|
|
129
136
|
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
132
|
-
throw new Error('A2 AI handlers must include ai.tool.
|
|
137
|
+
const toolExecutionHandler = aiHandlers['ai.tool.execution.requested']
|
|
138
|
+
if (toolExecutionHandler === undefined) {
|
|
139
|
+
throw new Error('A2 AI handlers must include ai.tool.execution.requested')
|
|
133
140
|
}
|
|
134
|
-
const
|
|
135
|
-
typeof
|
|
136
|
-
? { handler:
|
|
137
|
-
: { ...
|
|
141
|
+
const serializedToolExecutionHandler =
|
|
142
|
+
typeof toolExecutionHandler === 'function'
|
|
143
|
+
? { handler: toolExecutionHandler, lane: 'a2.chat.agent-tools' }
|
|
144
|
+
: { ...toolExecutionHandler, lane: 'a2.chat.agent-tools' }
|
|
138
145
|
|
|
139
146
|
export const chatScheduler =
|
|
140
147
|
process.env.VERCEL || process.env.A2_QUEUES
|
|
@@ -147,7 +154,7 @@ export const chatServer = createServer({
|
|
|
147
154
|
contract: chatAgent.contract,
|
|
148
155
|
handlers: {
|
|
149
156
|
...aiHandlers,
|
|
150
|
-
'ai.tool.
|
|
157
|
+
'ai.tool.execution.requested': serializedToolExecutionHandler,
|
|
151
158
|
'message.sent': {
|
|
152
159
|
lane: 'a2.chat.ingress',
|
|
153
160
|
handler: async (context) => {
|
|
@@ -1799,6 +1799,185 @@ button.danger {
|
|
|
1799
1799
|
flex: 0 0 auto;
|
|
1800
1800
|
}
|
|
1801
1801
|
|
|
1802
|
+
.agent-queue {
|
|
1803
|
+
margin: 0 1rem 0.85rem;
|
|
1804
|
+
border: 1px solid var(--line);
|
|
1805
|
+
border-radius: 9px;
|
|
1806
|
+
background: var(--bg);
|
|
1807
|
+
overflow: hidden;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
.agent-queue-header {
|
|
1811
|
+
display: flex;
|
|
1812
|
+
align-items: center;
|
|
1813
|
+
justify-content: space-between;
|
|
1814
|
+
gap: 0.75rem;
|
|
1815
|
+
padding: 0.55rem 0.8rem;
|
|
1816
|
+
border-bottom: 1px solid var(--line);
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
.agent-queue-header h3 {
|
|
1820
|
+
display: flex;
|
|
1821
|
+
align-items: center;
|
|
1822
|
+
gap: 0.45rem;
|
|
1823
|
+
margin: 0;
|
|
1824
|
+
color: var(--muted);
|
|
1825
|
+
font-size: 0.75rem;
|
|
1826
|
+
font-weight: 600;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
.agent-queue-header h3 span {
|
|
1830
|
+
min-width: 1.2rem;
|
|
1831
|
+
padding: 0 0.3rem;
|
|
1832
|
+
border-radius: 4px;
|
|
1833
|
+
background: var(--line);
|
|
1834
|
+
text-align: center;
|
|
1835
|
+
font-size: 0.68rem;
|
|
1836
|
+
font-variant-numeric: tabular-nums;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
.agent-queue-status {
|
|
1840
|
+
color: var(--muted);
|
|
1841
|
+
font-size: 0.7rem;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
.agent-queue-status.paused {
|
|
1845
|
+
color: #885b12;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
.agent-queue-list {
|
|
1849
|
+
max-height: 17rem;
|
|
1850
|
+
overflow-y: auto;
|
|
1851
|
+
overscroll-behavior: contain;
|
|
1852
|
+
list-style: none;
|
|
1853
|
+
margin: 0;
|
|
1854
|
+
padding: 0.15rem 0;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
.agent-queue-item + .agent-queue-item {
|
|
1858
|
+
border-top: 1px solid var(--line);
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
.agent-queue-row {
|
|
1862
|
+
display: grid;
|
|
1863
|
+
grid-template-columns: 1.25rem minmax(0, 1fr) auto;
|
|
1864
|
+
align-items: center;
|
|
1865
|
+
gap: 0.5rem;
|
|
1866
|
+
min-height: 2.7rem;
|
|
1867
|
+
padding: 0.25rem 0.55rem 0.25rem 0.8rem;
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
.agent-queue-number {
|
|
1871
|
+
color: var(--muted);
|
|
1872
|
+
font-size: 0.7rem;
|
|
1873
|
+
font-variant-numeric: tabular-nums;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
.agent-queue-text {
|
|
1877
|
+
margin: 0;
|
|
1878
|
+
overflow: hidden;
|
|
1879
|
+
white-space: nowrap;
|
|
1880
|
+
text-overflow: ellipsis;
|
|
1881
|
+
font-size: 0.83rem;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
.agent-queue-actions {
|
|
1885
|
+
display: flex;
|
|
1886
|
+
gap: 0.1rem;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
.agent-queue-actions button {
|
|
1890
|
+
display: grid;
|
|
1891
|
+
place-items: center;
|
|
1892
|
+
width: 1.9rem;
|
|
1893
|
+
height: 1.9rem;
|
|
1894
|
+
padding: 0;
|
|
1895
|
+
border: 0;
|
|
1896
|
+
border-radius: 5px;
|
|
1897
|
+
background: transparent;
|
|
1898
|
+
color: var(--muted);
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
.agent-queue-actions button:hover:not(:disabled),
|
|
1902
|
+
.agent-queue-actions button[aria-expanded='true'] {
|
|
1903
|
+
background: var(--line);
|
|
1904
|
+
color: var(--ink);
|
|
1905
|
+
filter: none;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
.agent-queue-actions button:disabled {
|
|
1909
|
+
opacity: 0.22;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
.agent-queue-actions .agent-queue-send {
|
|
1913
|
+
color: var(--accent);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
.agent-queue-actions .agent-queue-remove:hover:not(:disabled) {
|
|
1917
|
+
background: #f8e8e5;
|
|
1918
|
+
color: var(--danger);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
.agent-queue-error {
|
|
1922
|
+
margin: 0;
|
|
1923
|
+
padding: 0 0.75rem 0.65rem 2.55rem;
|
|
1924
|
+
font-size: 0.75rem;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
.agent-queue-item.editing {
|
|
1928
|
+
background: var(--surface);
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
.agent-queue-editor {
|
|
1932
|
+
gap: 0.45rem;
|
|
1933
|
+
margin: 0;
|
|
1934
|
+
padding: 0 0.75rem 0.65rem 2.55rem;
|
|
1935
|
+
border: 0;
|
|
1936
|
+
border-radius: 0;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
.agent-queue-editor textarea {
|
|
1940
|
+
width: 100%;
|
|
1941
|
+
min-width: 0;
|
|
1942
|
+
resize: vertical;
|
|
1943
|
+
font-size: 0.83rem;
|
|
1944
|
+
line-height: 1.5;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
.agent-queue-editor-actions {
|
|
1948
|
+
display: flex;
|
|
1949
|
+
align-items: center;
|
|
1950
|
+
justify-content: flex-end;
|
|
1951
|
+
gap: 0.35rem;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
.agent-queue-editor-actions span {
|
|
1955
|
+
margin-right: auto;
|
|
1956
|
+
color: var(--muted);
|
|
1957
|
+
font-size: 0.65rem;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
.agent-queue-editor-actions button {
|
|
1961
|
+
padding: 0.3rem 0.6rem;
|
|
1962
|
+
font-size: 0.72rem;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
@media (pointer: coarse) {
|
|
1966
|
+
.agent-queue-actions button {
|
|
1967
|
+
width: 2.25rem;
|
|
1968
|
+
height: 2.5rem;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
@media (max-width: 420px) {
|
|
1973
|
+
.agent-queue-editor {
|
|
1974
|
+
padding-left: 0.75rem;
|
|
1975
|
+
}
|
|
1976
|
+
.agent-queue-editor-actions span {
|
|
1977
|
+
display: none;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1802
1981
|
.agent-form {
|
|
1803
1982
|
margin: 0;
|
|
1804
1983
|
padding: 1rem;
|
package/package.json
CHANGED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import type { UIMessage } from 'ai'
|
|
2
|
+
import type { AIState } from './ai.ts'
|
|
3
|
+
import type { ControlCommand, InboxItem } from './ai-control.ts'
|
|
4
|
+
|
|
5
|
+
export type QueueCommand<M extends UIMessage> = Extract<
|
|
6
|
+
ControlCommand<M>,
|
|
7
|
+
{
|
|
8
|
+
action:
|
|
9
|
+
| 'send'
|
|
10
|
+
| 'edit'
|
|
11
|
+
| 'move'
|
|
12
|
+
| 'remove'
|
|
13
|
+
| 'send-now'
|
|
14
|
+
| 'steer'
|
|
15
|
+
| 'pause'
|
|
16
|
+
| 'resume'
|
|
17
|
+
}
|
|
18
|
+
>
|
|
19
|
+
export type PendingQueueCommand<M extends UIMessage> = {
|
|
20
|
+
commandId: string
|
|
21
|
+
command: QueueCommand<M>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const isQueueCommand = <M extends UIMessage>(
|
|
25
|
+
command: ControlCommand<M>,
|
|
26
|
+
): command is QueueCommand<M> =>
|
|
27
|
+
command.action === 'send' ||
|
|
28
|
+
command.action === 'edit' ||
|
|
29
|
+
command.action === 'move' ||
|
|
30
|
+
command.action === 'remove' ||
|
|
31
|
+
command.action === 'send-now' ||
|
|
32
|
+
command.action === 'steer' ||
|
|
33
|
+
command.action === 'pause' ||
|
|
34
|
+
command.action === 'resume'
|
|
35
|
+
|
|
36
|
+
const moveItem = <M extends UIMessage>({
|
|
37
|
+
items,
|
|
38
|
+
inputId,
|
|
39
|
+
beforeId,
|
|
40
|
+
}: {
|
|
41
|
+
items: InboxItem<M>[]
|
|
42
|
+
inputId: string
|
|
43
|
+
beforeId: string | null
|
|
44
|
+
}): InboxItem<M>[] => {
|
|
45
|
+
const from = items.findIndex((item) => item.id === inputId)
|
|
46
|
+
if (
|
|
47
|
+
from < 0 ||
|
|
48
|
+
inputId === beforeId ||
|
|
49
|
+
(beforeId !== null && !items.some((item) => item.id === beforeId))
|
|
50
|
+
)
|
|
51
|
+
return items
|
|
52
|
+
const next = [...items]
|
|
53
|
+
const [item] = next.splice(from, 1)
|
|
54
|
+
const to =
|
|
55
|
+
beforeId === null
|
|
56
|
+
? next.length
|
|
57
|
+
: next.findIndex((entry) => entry.id === beforeId)
|
|
58
|
+
next.splice(to, 0, item!)
|
|
59
|
+
return next
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function projectAIClientState<M extends UIMessage>(
|
|
63
|
+
state: AIState<M>,
|
|
64
|
+
): AIState<M> {
|
|
65
|
+
const pending = state.pendingQueueCommands ?? []
|
|
66
|
+
if (state.status === 'closed') return state
|
|
67
|
+
if (
|
|
68
|
+
pending.length === 0 &&
|
|
69
|
+
!state.inbox.items.some((item) => item.afterStepOf !== undefined)
|
|
70
|
+
)
|
|
71
|
+
return state
|
|
72
|
+
let messages = state.messages
|
|
73
|
+
let { items, paused } = state.inbox
|
|
74
|
+
let turnId = state.active?.turnId ?? null
|
|
75
|
+
let starting = false
|
|
76
|
+
let suspended =
|
|
77
|
+
state.active?.phase === 'paused' || state.active?.phase === 'pausing'
|
|
78
|
+
let messageIds: Set<string> | undefined
|
|
79
|
+
const show = (message: M): void => {
|
|
80
|
+
messageIds ??= new Set(messages.map((item) => item.id))
|
|
81
|
+
if (messageIds.has(message.id)) return
|
|
82
|
+
if (messages === state.messages) messages = state.messages.slice()
|
|
83
|
+
messages.push(message)
|
|
84
|
+
messageIds.add(message.id)
|
|
85
|
+
}
|
|
86
|
+
const admit = (): void => {
|
|
87
|
+
if (turnId !== null || paused || items.length === 0) return
|
|
88
|
+
let count = 0
|
|
89
|
+
for (const item of items) {
|
|
90
|
+
count += 1
|
|
91
|
+
show(item.message)
|
|
92
|
+
if (item.generate) {
|
|
93
|
+
turnId = item.id
|
|
94
|
+
starting = true
|
|
95
|
+
suspended = false
|
|
96
|
+
break
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (count > 0) items = items.slice(count)
|
|
100
|
+
}
|
|
101
|
+
for (const { command } of pending) {
|
|
102
|
+
switch (command.action) {
|
|
103
|
+
case 'send':
|
|
104
|
+
case 'steer': {
|
|
105
|
+
if (
|
|
106
|
+
items.some((item) => item.id === command.message.id) ||
|
|
107
|
+
turnId === command.message.id
|
|
108
|
+
)
|
|
109
|
+
break
|
|
110
|
+
const item = {
|
|
111
|
+
...(command.action === 'steer'
|
|
112
|
+
? { afterStepOf: command.turnId }
|
|
113
|
+
: {}),
|
|
114
|
+
id: command.message.id,
|
|
115
|
+
revision: 0,
|
|
116
|
+
message: command.message,
|
|
117
|
+
generate:
|
|
118
|
+
command.message.role === 'user' &&
|
|
119
|
+
(command.action === 'steer' || command.generate !== false),
|
|
120
|
+
}
|
|
121
|
+
items = command.action === 'steer' ? [item, ...items] : [...items, item]
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
case 'edit':
|
|
125
|
+
items = items.map((item) =>
|
|
126
|
+
item.id === command.message.id &&
|
|
127
|
+
item.revision === command.expectedRevision &&
|
|
128
|
+
item.message.role === command.message.role
|
|
129
|
+
? { ...item, message: command.message }
|
|
130
|
+
: item,
|
|
131
|
+
)
|
|
132
|
+
break
|
|
133
|
+
case 'move':
|
|
134
|
+
items = moveItem({
|
|
135
|
+
items,
|
|
136
|
+
inputId: command.inputId,
|
|
137
|
+
beforeId: command.beforeId,
|
|
138
|
+
})
|
|
139
|
+
break
|
|
140
|
+
case 'remove':
|
|
141
|
+
items = items.filter((item) => item.id !== command.inputId)
|
|
142
|
+
break
|
|
143
|
+
case 'send-now': {
|
|
144
|
+
if (turnId !== command.turnId) break
|
|
145
|
+
const selected = items.find((item) => item.id === command.inputId)
|
|
146
|
+
if (!selected || selected.message.role !== 'user') break
|
|
147
|
+
items = items.map((item) =>
|
|
148
|
+
item.id === selected.id
|
|
149
|
+
? {
|
|
150
|
+
...item,
|
|
151
|
+
generate: true,
|
|
152
|
+
...(turnId === null ? {} : { afterStepOf: turnId }),
|
|
153
|
+
}
|
|
154
|
+
: item,
|
|
155
|
+
)
|
|
156
|
+
items = moveItem({
|
|
157
|
+
items,
|
|
158
|
+
inputId: command.inputId,
|
|
159
|
+
beforeId: items[0]?.id ?? null,
|
|
160
|
+
})
|
|
161
|
+
break
|
|
162
|
+
}
|
|
163
|
+
case 'pause':
|
|
164
|
+
paused = true
|
|
165
|
+
if (command.when === 'now') suspended = true
|
|
166
|
+
break
|
|
167
|
+
case 'resume':
|
|
168
|
+
paused = false
|
|
169
|
+
suspended = false
|
|
170
|
+
break
|
|
171
|
+
}
|
|
172
|
+
admit()
|
|
173
|
+
}
|
|
174
|
+
items = items.filter((item) => {
|
|
175
|
+
if (item.afterStepOf === undefined) return true
|
|
176
|
+
show(item.message)
|
|
177
|
+
return false
|
|
178
|
+
})
|
|
179
|
+
return {
|
|
180
|
+
...state,
|
|
181
|
+
messages,
|
|
182
|
+
inbox: { items, paused },
|
|
183
|
+
starting: starting && !suspended,
|
|
184
|
+
}
|
|
185
|
+
}
|