@adecore/agents 0.48.0 → 0.48.1
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.
|
@@ -9,13 +9,13 @@ export interface MessageWords {
|
|
|
9
9
|
}
|
|
10
10
|
export interface ChatNoticeTargets {
|
|
11
11
|
chat(id: string): Promise<'idle' | 'running' | 'none'>;
|
|
12
|
-
|
|
12
|
+
wokenBy(id: string): readonly string[];
|
|
13
13
|
}
|
|
14
14
|
export interface ChatDelivery {
|
|
15
15
|
outcome: 'wake' | 'no-chat' | 'in-turn' | 'from-message';
|
|
16
16
|
waiting: number;
|
|
17
17
|
}
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function turnWokenBy(items: readonly ChatItem[], activeTurnId: string | null): readonly string[];
|
|
19
19
|
export declare function chatNoticeTargets(chats: Pick<ChatCore, 'get' | 'hasStored'>): ChatNoticeTargets;
|
|
20
20
|
export declare function deliverToChat(store: Pick<NoticeStore, 'put'>, targets: ChatNoticeTargets, notice: Omit<Notice, 'createdAt'>): Promise<ChatDelivery>;
|
|
21
21
|
export interface NoticeChat {
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
/*
|
|
2
|
-
*
|
|
3
|
-
* read each other from waking each other forever. It rides on the turn,
|
|
4
|
-
* Between turns the last one counts, since work it left running (a
|
|
5
|
-
* write, and a turn the CLI opened by itself takes the step of the
|
|
2
|
+
* Who sent the message that opened the turn a chat is running, empty when no message opened it,
|
|
3
|
+
* which stops two agents that read each other from waking each other forever. It rides on the turn,
|
|
4
|
+
* so it survives a restart. Between turns the last one counts, since work it left running (a
|
|
5
|
+
* background subagent) may still write, and a turn the CLI opened by itself takes the step of the
|
|
6
|
+
* turn before it.
|
|
6
7
|
*/
|
|
7
|
-
export function
|
|
8
|
+
export function turnWokenBy(items, activeTurnId) {
|
|
8
9
|
const from = activeTurnId === null ? items.findLastIndex((item) => item.kind === 'turn') : items.findIndex((item) => item.id === activeTurnId);
|
|
9
10
|
for (let i = from; i >= 0; i--) {
|
|
10
11
|
const turn = items[i];
|
|
11
12
|
if (turn.kind !== 'turn') {
|
|
12
13
|
continue;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const senders = turn.messageFrom ?? [];
|
|
16
|
+
if (senders.length > 0) {
|
|
17
|
+
return senders;
|
|
16
18
|
}
|
|
17
19
|
if (turn.origin !== 'agent' || turn.taskIds !== undefined || turn.summaryFor !== undefined) {
|
|
18
|
-
return
|
|
20
|
+
return [];
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
|
-
return
|
|
23
|
+
return [];
|
|
22
24
|
}
|
|
23
25
|
/* The chats of a core as a message sees them. */
|
|
24
26
|
export function chatNoticeTargets(chats) {
|
|
@@ -31,17 +33,19 @@ export function chatNoticeTargets(chats) {
|
|
|
31
33
|
// A chat nobody has loaded is idle: the turn opens on the thread the host reads back from disk.
|
|
32
34
|
return (await chats.hasStored(id)) ? 'idle' : 'none';
|
|
33
35
|
},
|
|
34
|
-
|
|
36
|
+
wokenBy: (id) => {
|
|
35
37
|
const session = chats.get(id);
|
|
36
|
-
return session
|
|
38
|
+
return session === undefined ? [] : turnWokenBy(session.thread.list(), session.info.activeTurnId);
|
|
37
39
|
}
|
|
38
40
|
};
|
|
39
41
|
}
|
|
40
42
|
/*
|
|
41
43
|
* One message on its way to a chat, and whether the chat owes a turn on it. A chat between turns gets
|
|
42
44
|
* one, as for a settled task, or the message sits there until someone happens to prompt that chat.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
+
* A turn a message opened wakes nobody with a message of its own, except the sender it answers,
|
|
46
|
+
* once: only while that sender's own turn was opened by no message, so a question and its answer
|
|
47
|
+
* take two steps and the turn the answer opens wakes nobody. A node that runs something else, such
|
|
48
|
+
* as a terminal, is the host's to deliver to before this.
|
|
45
49
|
*/
|
|
46
50
|
export async function deliverToChat(store, targets, notice) {
|
|
47
51
|
const waiting = await store.put(notice);
|
|
@@ -52,7 +56,9 @@ export async function deliverToChat(store, targets, notice) {
|
|
|
52
56
|
if (chat === 'running') {
|
|
53
57
|
return { outcome: 'in-turn', waiting };
|
|
54
58
|
}
|
|
55
|
-
|
|
59
|
+
const wokenBy = targets.wokenBy(notice.from);
|
|
60
|
+
const answer = wokenBy.includes(notice.targetId) && targets.wokenBy(notice.targetId).length === 0;
|
|
61
|
+
if (wokenBy.length > 0 && !answer) {
|
|
56
62
|
return { outcome: 'from-message', waiting };
|
|
57
63
|
}
|
|
58
64
|
return { outcome: 'wake', waiting };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/agents",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.1",
|
|
4
4
|
"description": "Node-compatible agent chat hosts, providers, accounts, usage and durable task coordination.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"test": "bun --conditions=source test"
|
|
94
94
|
},
|
|
95
95
|
"dependencies": {
|
|
96
|
-
"@adecore/agent-contracts": "0.48.
|
|
97
|
-
"@adecore/intelligent-ui": "0.48.
|
|
96
|
+
"@adecore/agent-contracts": "0.48.1",
|
|
97
|
+
"@adecore/intelligent-ui": "0.48.1",
|
|
98
98
|
"zod": "^4.6.7"
|
|
99
99
|
},
|
|
100
100
|
"devDependencies": {
|
|
@@ -18,8 +18,8 @@ export interface MessageWords {
|
|
|
18
18
|
export interface ChatNoticeTargets {
|
|
19
19
|
/* What the host holds under this id: a chat between turns, one that is in a turn, or no chat at all. */
|
|
20
20
|
chat(id: string): Promise<'idle' | 'running' | 'none'>;
|
|
21
|
-
/*
|
|
22
|
-
|
|
21
|
+
/* The senders whose message opened the turn this node is running, empty for a turn no message opened. */
|
|
22
|
+
wokenBy(id: string): readonly string[];
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/*
|
|
@@ -33,12 +33,13 @@ export interface ChatDelivery {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/*
|
|
36
|
-
*
|
|
37
|
-
* read each other from waking each other forever. It rides on the turn,
|
|
38
|
-
* Between turns the last one counts, since work it left running (a
|
|
39
|
-
* write, and a turn the CLI opened by itself takes the step of the
|
|
36
|
+
* Who sent the message that opened the turn a chat is running, empty when no message opened it,
|
|
37
|
+
* which stops two agents that read each other from waking each other forever. It rides on the turn,
|
|
38
|
+
* so it survives a restart. Between turns the last one counts, since work it left running (a
|
|
39
|
+
* background subagent) may still write, and a turn the CLI opened by itself takes the step of the
|
|
40
|
+
* turn before it.
|
|
40
41
|
*/
|
|
41
|
-
export function
|
|
42
|
+
export function turnWokenBy(items: readonly ChatItem[], activeTurnId: string | null): readonly string[] {
|
|
42
43
|
const from = activeTurnId === null ? items.findLastIndex((item) => item.kind === 'turn') : items.findIndex((item) => item.id === activeTurnId);
|
|
43
44
|
|
|
44
45
|
for (let i = from; i >= 0; i--) {
|
|
@@ -48,16 +49,18 @@ export function turnFromMessage(items: readonly ChatItem[], activeTurnId: string
|
|
|
48
49
|
continue;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
const senders = turn.messageFrom ?? [];
|
|
53
|
+
|
|
54
|
+
if (senders.length > 0) {
|
|
55
|
+
return senders;
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
if (turn.origin !== 'agent' || turn.taskIds !== undefined || turn.summaryFor !== undefined) {
|
|
56
|
-
return
|
|
59
|
+
return [];
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
return
|
|
63
|
+
return [];
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
/* The chats of a core as a message sees them. */
|
|
@@ -73,10 +76,10 @@ export function chatNoticeTargets(chats: Pick<ChatCore, 'get' | 'hasStored'>): C
|
|
|
73
76
|
// A chat nobody has loaded is idle: the turn opens on the thread the host reads back from disk.
|
|
74
77
|
return (await chats.hasStored(id)) ? 'idle' : 'none';
|
|
75
78
|
},
|
|
76
|
-
|
|
79
|
+
wokenBy: (id) => {
|
|
77
80
|
const session = chats.get(id);
|
|
78
81
|
|
|
79
|
-
return session
|
|
82
|
+
return session === undefined ? [] : turnWokenBy(session.thread.list(), session.info.activeTurnId);
|
|
80
83
|
}
|
|
81
84
|
};
|
|
82
85
|
}
|
|
@@ -84,8 +87,10 @@ export function chatNoticeTargets(chats: Pick<ChatCore, 'get' | 'hasStored'>): C
|
|
|
84
87
|
/*
|
|
85
88
|
* One message on its way to a chat, and whether the chat owes a turn on it. A chat between turns gets
|
|
86
89
|
* one, as for a settled task, or the message sits there until someone happens to prompt that chat.
|
|
87
|
-
*
|
|
88
|
-
*
|
|
90
|
+
* A turn a message opened wakes nobody with a message of its own, except the sender it answers,
|
|
91
|
+
* once: only while that sender's own turn was opened by no message, so a question and its answer
|
|
92
|
+
* take two steps and the turn the answer opens wakes nobody. A node that runs something else, such
|
|
93
|
+
* as a terminal, is the host's to deliver to before this.
|
|
89
94
|
*/
|
|
90
95
|
export async function deliverToChat(store: Pick<NoticeStore, 'put'>, targets: ChatNoticeTargets, notice: Omit<Notice, 'createdAt'>): Promise<ChatDelivery> {
|
|
91
96
|
const waiting = await store.put(notice);
|
|
@@ -99,7 +104,10 @@ export async function deliverToChat(store: Pick<NoticeStore, 'put'>, targets: Ch
|
|
|
99
104
|
return { outcome: 'in-turn', waiting };
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
const wokenBy = targets.wokenBy(notice.from);
|
|
108
|
+
const answer = wokenBy.includes(notice.targetId) && targets.wokenBy(notice.targetId).length === 0;
|
|
109
|
+
|
|
110
|
+
if (wokenBy.length > 0 && !answer) {
|
|
103
111
|
return { outcome: 'from-message', waiting };
|
|
104
112
|
}
|
|
105
113
|
|