@primusclaw/personal-agent 0.5.0 → 0.7.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/dist/index.js +536 -66
- package/dist/types/assistant/chat.d.ts +2 -0
- package/dist/types/assistant/choice-clicks.d.ts +35 -0
- package/dist/types/assistant/events.d.ts +17 -0
- package/dist/types/assistant/index.d.ts +1 -0
- package/dist/types/assistant/say.d.ts +12 -4
- package/dist/types/assistant/views.d.ts +22 -0
- package/dist/types/language/chosen.d.ts +24 -0
- package/dist/types/language/group.d.ts +18 -0
- package/dist/types/locales/assistant-cards.d.ts +15 -0
- package/dist/types/locales/group-language.d.ts +32 -0
- package/dist/types/locales/group.d.ts +1 -1
- package/dist/types/locales/terminal-approvals.d.ts +5 -0
- package/dist/types/ports/store/assistant.d.ts +27 -1
- package/dist/types/ports/store/group-language.d.ts +20 -0
- package/dist/types/ports/store/index.d.ts +3 -0
- package/dist/types/ports/surface.d.ts +6 -0
- package/dist/types/protocol/claw-api/assistant-events.d.ts +24 -2
- package/dist/types/remote-view/terminal-ask-view.d.ts +11 -1
- package/dist/types/routing/chat.d.ts +16 -2
- package/dist/types/routing/deps.d.ts +1 -1
- package/dist/types/routing/group-language.d.ts +25 -0
- package/dist/types/routing/group-router.d.ts +3 -1
- package/dist/types/routing/personal-text.d.ts +2 -1
- package/dist/types/routing/turn-runner.d.ts +6 -0
- package/dist/types/routing/turn-start.d.ts +7 -2
- package/dist/types/turn/engine/context.d.ts +4 -2
- package/package.json +1 -1
|
@@ -89,6 +89,8 @@ export declare class AssistantChat {
|
|
|
89
89
|
forget(userId: string): void;
|
|
90
90
|
/** What a message of files and no words says to the assistant. */
|
|
91
91
|
filesOnly(lang: Lang): string;
|
|
92
|
+
/** The language the person chose to be answered in, as the message's `channel_context` carries it (none: nothing is sent). */
|
|
93
|
+
private chosenLanguage;
|
|
92
94
|
private langOf;
|
|
93
95
|
/**
|
|
94
96
|
* A message of a person the assistant understands, with the chat's context. True when the assistant has it; otherwise the
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ClickHandler, ClickRoute } from "../ports/routing.js";
|
|
2
|
+
import type { ChoiceCard } from "../ports/store/assistant.js";
|
|
3
|
+
import type { StorePort } from "../ports/store/index.js";
|
|
4
|
+
import type { Logger } from "../ports/logger.js";
|
|
5
|
+
import type { InboundMessage } from "../ports/surface.js";
|
|
6
|
+
export declare const CHOICE_VERB = "assistant_choice";
|
|
7
|
+
/** The id of a choice card's input, whose value is the picked options' places on the card ("0", "0,2"). */
|
|
8
|
+
export declare const CHOICE_INPUT = "choice";
|
|
9
|
+
export interface ChoiceClickDeps {
|
|
10
|
+
readonly store: Pick<StorePort, "assistantButtons">;
|
|
11
|
+
/** Handles `message` as the router handles one the person typed (`Router.handle`); throws when it was not handled. */
|
|
12
|
+
readonly deliver: (message: InboundMessage) => Promise<void>;
|
|
13
|
+
readonly logger: Logger;
|
|
14
|
+
}
|
|
15
|
+
/** The options `value` picks on `card` (each named by its place), in the card's order; a place it never had is no pick. */
|
|
16
|
+
export declare function pickedOn(card: Pick<ChoiceCard, "options">, value: unknown): string[];
|
|
17
|
+
/**
|
|
18
|
+
* The message a press sends: the presser's, in the chat it was pressed in, saying `text`, as a typed one is (to the bot, in a
|
|
19
|
+
* group), but marked `choice`, so no word of it is read as a command.
|
|
20
|
+
*/
|
|
21
|
+
export declare function typedMessage(click: Pick<ClickRoute, "action" | "sender">, text: string, id: string): InboundMessage;
|
|
22
|
+
export declare class AssistantChoiceClicks implements ClickHandler {
|
|
23
|
+
private readonly deps;
|
|
24
|
+
readonly verbs: readonly ["assistant_choice"];
|
|
25
|
+
constructor(deps: ChoiceClickDeps);
|
|
26
|
+
/** A 1:1 chat: answered with no Claw key (the message it sends is checked as any typed one is). */
|
|
27
|
+
beforeCredential(click: ClickRoute): Promise<boolean>;
|
|
28
|
+
inGroup(click: ClickRoute): Promise<boolean>;
|
|
29
|
+
press(click: ClickRoute): Promise<void>;
|
|
30
|
+
/** The card back, open, with `note` under its options. */
|
|
31
|
+
private again;
|
|
32
|
+
/** The card replaced by what was picked and sent (said as a line too when `announce`). */
|
|
33
|
+
private sent;
|
|
34
|
+
private model;
|
|
35
|
+
}
|
|
@@ -41,6 +41,23 @@ export declare function confirmWriteOf(event: Event): ConfirmWrite | undefined;
|
|
|
41
41
|
* line that is no command, else the defaults in the person's language (`lang`; without it, the language of `text`).
|
|
42
42
|
*/
|
|
43
43
|
export declare function suggestedRepliesOf(event: Event, text: string, styleOn: boolean, lang?: Lang): SuggestedReply[];
|
|
44
|
+
/** The fewest options a choice card shows (Claw sends from 2). */
|
|
45
|
+
export declare const CHOICE_OPTIONS_MIN = 2;
|
|
46
|
+
/** The most options a choice card shows (Claw sends at most 10). */
|
|
47
|
+
export declare const CHOICE_OPTIONS_MAX = 10;
|
|
48
|
+
/** Code points of one option (Claw sends at most 80): one line beside a radio button or a check box. */
|
|
49
|
+
export declare const CHOICE_OPTION_MAX = 80;
|
|
50
|
+
/** A choice an `assistant.say` puts to the person: the options, and whether several may be picked. */
|
|
51
|
+
export interface SayChoice {
|
|
52
|
+
readonly options: readonly string[];
|
|
53
|
+
readonly multiple: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The choice an `assistant.say` puts to the person (`choices`); undefined when it carries none this worker draws. Read whole or not
|
|
57
|
+
* at all: an option that is not one plain line of text, or too many or too few of them, and there is no card (the words still name
|
|
58
|
+
* the options, for the person to type).
|
|
59
|
+
*/
|
|
60
|
+
export declare function choiceOf(event: Event): SayChoice | undefined;
|
|
44
61
|
export interface RemoteNotice {
|
|
45
62
|
/** `resolved`: a question a terminal asked no longer waits here (answered there, or given up); `resolved` says which. */
|
|
46
63
|
readonly kind: "done" | "needs_you" | "error" | "resolved";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** What the assembly wires of the assistant (one line each; the views and work-item text are exported from the package root). */
|
|
2
2
|
export { AssistantActionClicks } from "./action-handler.js";
|
|
3
3
|
export { AssistantActions } from "./actions.js";
|
|
4
|
+
export { AssistantChoiceClicks } from "./choice-clicks.js";
|
|
4
5
|
export { Board } from "./board.js";
|
|
5
6
|
export { AssistantChat as AssistantChatService } from "./chat.js";
|
|
6
7
|
export type { AssistantChatOptions } from "./chat.js";
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* receipt already shows of it (`shown`). It is an AI answer: the AI label and the thumbs under it, a citation for each source it
|
|
6
6
|
* cites as [n], and the replies to tap under the last of its messages when no card follows.
|
|
7
7
|
* - When the event's item holds a result that waits, a card with Accept and Stop; when it holds a write for the person, a card
|
|
8
|
-
* that lets it through or not; when it puts a command to the person, a card with Run and Cancel
|
|
9
|
-
* buttons mean something). A card holds the words only
|
|
8
|
+
* that lets it through or not; when it puts a command to the person, a card with Run and Cancel; when it puts a choice to the
|
|
9
|
+
* person, a card with the options and Send (each recorded first, so its buttons mean something). A card holds the words only
|
|
10
|
+
* when they are short and not shown already.
|
|
10
11
|
* - Then a card for each file the item's worker delivered, with Claw's own download link.
|
|
11
12
|
*
|
|
12
13
|
* Every word of this worker's own is in the person's language, never in that of what was said.
|
|
@@ -51,10 +52,17 @@ export declare class SayPlanner {
|
|
|
51
52
|
/** The replies to tap, under the last of what was said, when no card follows. */
|
|
52
53
|
private withReplies;
|
|
53
54
|
/**
|
|
54
|
-
* The card with buttons the event asks for, recorded first: a result that waits, a write Claw holds, or a
|
|
55
|
-
* person; undefined for none. `holdsWords`: the card shows the words, which are then not said apart.
|
|
55
|
+
* The card with buttons the event asks for, recorded first: a result that waits, a write Claw holds, a command or a choice put
|
|
56
|
+
* to the person; undefined for none. `holdsWords`: the card shows the words, which are then not said apart.
|
|
56
57
|
*/
|
|
57
58
|
private card;
|
|
59
|
+
/** A write Claw holds for the person (Allow, Deny) or a command put to them (Run, Cancel), recorded first; undefined for none. */
|
|
60
|
+
private askCard;
|
|
61
|
+
/**
|
|
62
|
+
* A choice put to the person (`choices`): its options and Send, recorded first (what a press sends is read from the record).
|
|
63
|
+
* Its Send sends what was picked as the person's own message.
|
|
64
|
+
*/
|
|
65
|
+
private choiceCard;
|
|
58
66
|
/** What was said, as AI answers with thumbs, each with the citations its own words cite (when the whole text cites at all). */
|
|
59
67
|
private said;
|
|
60
68
|
/** A result that waits: Accept and Stop, and (a task whose result names a pull request) auto-reply on or off. */
|
|
@@ -68,9 +68,31 @@ export interface ConfirmWriteModel {
|
|
|
68
68
|
readonly allowLabel: string;
|
|
69
69
|
readonly lang: string;
|
|
70
70
|
}
|
|
71
|
+
/** A choice the assistant put to the person: its options as radio buttons (or check boxes) and Send. */
|
|
72
|
+
export interface ChoiceCardModel {
|
|
73
|
+
/** The words, when the card holds them ("" when they were said apart). */
|
|
74
|
+
readonly text: string;
|
|
75
|
+
readonly cardId: string;
|
|
76
|
+
readonly options: readonly string[];
|
|
77
|
+
/** Several may be picked (check boxes); else one (radio buttons). */
|
|
78
|
+
readonly multiple: boolean;
|
|
79
|
+
/** One line under the options when the card comes back unanswered ("" none). */
|
|
80
|
+
readonly note: string;
|
|
81
|
+
/** "zh" or "en"; null to follow what was said. */
|
|
82
|
+
readonly lang: string | null;
|
|
83
|
+
}
|
|
84
|
+
/** What replaces a choice card once the person sent what they picked: what they sent, under the words it answered. */
|
|
85
|
+
export interface ChoiceChosenModel {
|
|
86
|
+
readonly text: string;
|
|
87
|
+
/** The options picked, in the card's order. */
|
|
88
|
+
readonly chosen: readonly string[];
|
|
89
|
+
readonly lang: string | null;
|
|
90
|
+
}
|
|
71
91
|
declare module "../ports/surface.js" {
|
|
72
92
|
interface ViewMap {
|
|
73
93
|
actionCard: ActionCardModel;
|
|
94
|
+
choiceCard: ChoiceCardModel;
|
|
95
|
+
choiceChosen: ChoiceChosenModel;
|
|
74
96
|
board: BoardModel;
|
|
75
97
|
confirmWrite: ConfirmWriteModel;
|
|
76
98
|
workCard: WorkCardModel;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The language a person chose to be answered in (`/lang zh|en`), as it goes to Claw with each of their messages, so that the
|
|
3
|
+
* model's answer is in it as well as the agent's own texts.
|
|
4
|
+
*
|
|
5
|
+
* Only a choice the person said counts: a language learnt from what they write (`learnLang`), and `/lang auto`, are no
|
|
6
|
+
* choice, nothing is sent, and Claw reads the language from the message as before. To the assistant the choice goes in the
|
|
7
|
+
* message's `channel_context` (`reply_language`); to a plain session (a 1:1 chat without the assistant) as the line Claw's
|
|
8
|
+
* assistant uses for it, after the message (`replyLanguageLine`). A group's turn carries the group's language in that line
|
|
9
|
+
* instead (`groupChosenLang`): in a group a person's own choice is not read.
|
|
10
|
+
*/
|
|
11
|
+
import type { Lang } from "../locales/lang.js";
|
|
12
|
+
/** What of a person's preferences says the choice (`Prefs.lang`, `Prefs.langSet`). */
|
|
13
|
+
export interface LangPrefs {
|
|
14
|
+
readonly lang: string;
|
|
15
|
+
readonly langSet: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** The language the person chose, or undefined when they chose none. */
|
|
18
|
+
export declare function chosenLang(prefs: LangPrefs): Lang | undefined;
|
|
19
|
+
/** The choice as `channel_context` carries it (`{ reply_language: "zh" }`); empty when there is none. */
|
|
20
|
+
export declare function replyLanguageContext(prefs: LangPrefs): {
|
|
21
|
+
reply_language?: Lang;
|
|
22
|
+
};
|
|
23
|
+
/** The line after a message to a plain session that names the language to answer in: `[Reply language: English]`. */
|
|
24
|
+
export declare function replyLanguageLine(lang: Lang): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a group chat says to set the language it is answered in: `/lang zh|en|auto` (`/lang` alone asks which it is), or one of a
|
|
3
|
+
* few sentences that name the group ("这个群用英文", "本群跟随提问者的语言", "use English in this group", "set the group language to
|
|
4
|
+
* Chinese"). Only the whole message counts. A sentence that does not name the group ("用英文回复", "answer in English") is no
|
|
5
|
+
* setting: it asks for that one answer in that language, and goes to the agent as it is.
|
|
6
|
+
*/
|
|
7
|
+
import type { GroupLanguage } from "../ports/store/group-language.js";
|
|
8
|
+
/** A group's language command: set it, say which it is, or how to write it. */
|
|
9
|
+
export type GroupLangCommand = {
|
|
10
|
+
readonly kind: "set";
|
|
11
|
+
readonly lang: GroupLanguage;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "status";
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "usage";
|
|
16
|
+
};
|
|
17
|
+
/** The group's language command `text` is (`/lang …` or a sentence that sets it), or undefined when it is none. */
|
|
18
|
+
export declare function parseGroupLang(text: string): GroupLangCommand | undefined;
|
|
@@ -67,6 +67,21 @@ export declare const assistantCardText: import("./catalog.js").Catalog<{
|
|
|
67
67
|
askedTitle: (p: {
|
|
68
68
|
n: number;
|
|
69
69
|
}) => string;
|
|
70
|
+
choiceTitle: string;
|
|
71
|
+
choiceTitleMany: string;
|
|
72
|
+
choiceSend: string;
|
|
73
|
+
choiceTyped: string;
|
|
74
|
+
choiceNone: string;
|
|
75
|
+
choiceSent: (p: {
|
|
76
|
+
answer: string;
|
|
77
|
+
}) => string;
|
|
78
|
+
choiceGone: string;
|
|
79
|
+
choiceSending: (p: {
|
|
80
|
+
answer: string;
|
|
81
|
+
}) => string;
|
|
82
|
+
choiceFailed: string;
|
|
83
|
+
/** How the options picked are joined into the person's message. */
|
|
84
|
+
choiceJoin: string;
|
|
70
85
|
version: (p: {
|
|
71
86
|
version: number;
|
|
72
87
|
}) => string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { GroupLanguage } from "../ports/store/group-language.js";
|
|
2
|
+
/** The announcement, in the chat, of a change of its language by `name` ("" when the channel gave none). */
|
|
3
|
+
export declare function groupLanguageSet(lang: GroupLanguage, name: string): string;
|
|
4
|
+
/** The card the first @mention in a chat with no language puts to its members. */
|
|
5
|
+
export declare const GROUP_LANGUAGE_CARD: {
|
|
6
|
+
title: string;
|
|
7
|
+
text: (p: {
|
|
8
|
+
bot: string;
|
|
9
|
+
}) => string;
|
|
10
|
+
buttons: {
|
|
11
|
+
zh: string;
|
|
12
|
+
en: string;
|
|
13
|
+
auto: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare const groupLanguageText: import("./catalog.js").Catalog<{
|
|
17
|
+
isZh: (p: {
|
|
18
|
+
bot: string;
|
|
19
|
+
}) => string;
|
|
20
|
+
isEn: (p: {
|
|
21
|
+
bot: string;
|
|
22
|
+
}) => string;
|
|
23
|
+
isAuto: (p: {
|
|
24
|
+
bot: string;
|
|
25
|
+
}) => string;
|
|
26
|
+
isNone: (p: {
|
|
27
|
+
bot: string;
|
|
28
|
+
}) => string;
|
|
29
|
+
usage: (p: {
|
|
30
|
+
bot: string;
|
|
31
|
+
}) => string;
|
|
32
|
+
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** What a group chat is told
|
|
1
|
+
/** What a group chat is told, in the group's language (`Chat.lang`). */
|
|
2
2
|
export declare const groupText: import("./catalog.js").Catalog<{
|
|
3
3
|
githubOneToOne: string;
|
|
4
4
|
oneToOneOnly: (p: {
|
|
@@ -20,7 +20,11 @@ export declare const terminalApprovalText: import("./catalog.js").Catalog<{
|
|
|
20
20
|
name: string;
|
|
21
21
|
}) => string;
|
|
22
22
|
firstAnswerWins: string;
|
|
23
|
+
afterCard: string;
|
|
24
|
+
onInput: string;
|
|
23
25
|
tooLong: string;
|
|
26
|
+
tooLongAfterCard: string;
|
|
27
|
+
tooLongOnInput: string;
|
|
24
28
|
questionOf: (p: {
|
|
25
29
|
n: number;
|
|
26
30
|
of: number;
|
|
@@ -31,6 +35,7 @@ export declare const terminalApprovalText: import("./catalog.js").Catalog<{
|
|
|
31
35
|
optionNoStop: string;
|
|
32
36
|
optionPlanApprove: string;
|
|
33
37
|
optionPlanKeep: string;
|
|
38
|
+
optionToTerminal: string;
|
|
34
39
|
answeredInTerminal: string;
|
|
35
40
|
noLongerWaiting: string;
|
|
36
41
|
answeredInTerminalLine: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cards and messages of the assistant (tables `assistant_cards`, `auto_replies`, `assistant_action_cards`,
|
|
3
|
-
* `assistant_confirm_cards`, `assistant_messages`). A card's buttons carry only its id; what a press means is read
|
|
3
|
+
* `assistant_confirm_cards`, `assistant_choice_cards`, `assistant_messages`). A card's buttons carry only its id; what a press means is read
|
|
4
4
|
* from here, so a forged, old or someone else's card changes nothing.
|
|
5
5
|
*/
|
|
6
6
|
import type { JsonObject } from "./json.js";
|
|
@@ -56,6 +56,21 @@ export interface ConfirmCard {
|
|
|
56
56
|
readonly state: CardState;
|
|
57
57
|
readonly createdAt: number;
|
|
58
58
|
}
|
|
59
|
+
/** A choice the assistant put to the person: what was asked, the options, and (once sent) what was picked. */
|
|
60
|
+
export interface ChoiceCard {
|
|
61
|
+
readonly cardId: string;
|
|
62
|
+
readonly userId: string;
|
|
63
|
+
/** The words the card holds ("" when they were said apart). */
|
|
64
|
+
readonly text: string;
|
|
65
|
+
readonly options: readonly string[];
|
|
66
|
+
readonly multiple: boolean;
|
|
67
|
+
/** What was picked, in the card's order: being sent ('busy') or sent ('done'); empty before the first press. */
|
|
68
|
+
readonly chosen: readonly string[];
|
|
69
|
+
/** 'open'; 'busy' while a press sends it; 'done' once the router took it. */
|
|
70
|
+
readonly state: CardState;
|
|
71
|
+
readonly lang: string;
|
|
72
|
+
readonly createdAt: number;
|
|
73
|
+
}
|
|
59
74
|
export interface AssistantMessage {
|
|
60
75
|
readonly userId: string;
|
|
61
76
|
/** The actions (and handoffs) the message declared. */
|
|
@@ -94,6 +109,17 @@ export interface AssistantButtonRepository {
|
|
|
94
109
|
/** Null once it is older than CONFIRM_CARD_KEEP_SECONDS. */
|
|
95
110
|
getConfirmCard(cardId: string): ConfirmCard | null;
|
|
96
111
|
moveConfirmCard(cardId: string, state: CardState, options?: MoveOptions): boolean;
|
|
112
|
+
addChoiceCard(userId: string, choice: {
|
|
113
|
+
readonly text: string;
|
|
114
|
+
readonly options: readonly string[];
|
|
115
|
+
readonly multiple: boolean;
|
|
116
|
+
readonly lang: string;
|
|
117
|
+
}): string;
|
|
118
|
+
/** Null once it is older than ASSISTANT_MESSAGE_KEEP_SECONDS. */
|
|
119
|
+
getChoiceCard(cardId: string): ChoiceCard | null;
|
|
120
|
+
/** An open card is claimed by a press to send `chosen` (it moves to 'busy'); true if this call claimed it, so of two presses exactly one sends. */
|
|
121
|
+
claimChoiceCard(cardId: string, chosen: readonly string[]): boolean;
|
|
122
|
+
moveChoiceCard(cardId: string, state: CardState, options?: MoveOptions): boolean;
|
|
97
123
|
}
|
|
98
124
|
export interface AssistantMessageRepository {
|
|
99
125
|
/** A message this worker sent to the person's assistant, with the actions it declared and the Claw users it named. */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The language each group chat is answered in, as its members set it (`@bot /lang en|zh|auto`, "这个群用英文", or a pick on the
|
|
3
|
+
* card the bot posts the first time it is @mentioned there). `auto`: each question is answered in the language it is asked in.
|
|
4
|
+
* A chat the card was put to and nobody answered yet is kept as asked (`lang` ""), so it is never asked again. Who set it is
|
|
5
|
+
* never stored: the announcement names them once, in the chat.
|
|
6
|
+
*/
|
|
7
|
+
export type GroupLanguage = "zh" | "en" | "auto";
|
|
8
|
+
export interface GroupLanguageRepository {
|
|
9
|
+
/** The chat's language; "" when it was asked and nobody chose; null when it was never asked nor set. */
|
|
10
|
+
groupLanguage(conversationId: string): GroupLanguage | "" | null;
|
|
11
|
+
/** Sets it (anyone in the chat may). */
|
|
12
|
+
setGroupLanguage(conversationId: string, lang: GroupLanguage): void;
|
|
13
|
+
/**
|
|
14
|
+
* Records that the chat is asked now: true when this call recorded it (it was never asked nor set), false when it was already, in
|
|
15
|
+
* one step, so that two @mentions at once put one card.
|
|
16
|
+
*/
|
|
17
|
+
markGroupLanguageAsked(conversationId: string): boolean;
|
|
18
|
+
/** The question could not be put after all: the chat is asked again next time (nothing is undone once somebody chose). */
|
|
19
|
+
unmarkGroupLanguageAsked(conversationId: string): void;
|
|
20
|
+
}
|
|
@@ -14,6 +14,7 @@ import type { AssistantButtonRepository, AssistantCardRepository, AssistantMessa
|
|
|
14
14
|
import type { FeedbackRepository } from "./feedback.js";
|
|
15
15
|
import type { GithubConfirmationRepository, ReviewDelegationRepository } from "./github.js";
|
|
16
16
|
import type { GroupContextRepository } from "./group-context.js";
|
|
17
|
+
import type { GroupLanguageRepository } from "./group-language.js";
|
|
17
18
|
import type { IdempotencyRepository } from "./idempotency.js";
|
|
18
19
|
import type { LinkRepository } from "./link.js";
|
|
19
20
|
import type { NotifySubscriptionRepository } from "./notify.js";
|
|
@@ -33,6 +34,7 @@ export interface StorePort {
|
|
|
33
34
|
readonly approvals: ApprovalRepository;
|
|
34
35
|
readonly observe: ObserveRepository;
|
|
35
36
|
readonly groupContext: GroupContextRepository;
|
|
37
|
+
readonly groupLanguage: GroupLanguageRepository;
|
|
36
38
|
readonly assistantCards: AssistantCardRepository;
|
|
37
39
|
readonly assistantButtons: AssistantButtonRepository;
|
|
38
40
|
readonly assistantMessages: AssistantMessageRepository;
|
|
@@ -55,6 +57,7 @@ export type * from "./assistant.js";
|
|
|
55
57
|
export type * from "./feedback.js";
|
|
56
58
|
export type * from "./github.js";
|
|
57
59
|
export type * from "./group-context.js";
|
|
60
|
+
export type * from "./group-language.js";
|
|
58
61
|
export type * from "./idempotency.js";
|
|
59
62
|
export type * from "./link.js";
|
|
60
63
|
export type * from "./notify.js";
|
|
@@ -134,6 +134,12 @@ export interface InboundMessage extends InboundBase {
|
|
|
134
134
|
*/
|
|
135
135
|
readonly unreadableFiles: readonly string[];
|
|
136
136
|
readonly provenance: Provenance;
|
|
137
|
+
/**
|
|
138
|
+
* What was picked on a choice card the assistant put (`assistant_choice`): an answer, never read as a command. The menu's
|
|
139
|
+
* words and help's phrases ("New", "重新开始", "Stop", "关闭记忆") stay the words picked; it goes where a typed message that
|
|
140
|
+
* is no command goes (the assistant, or the agent a 1:1 chat is attached to).
|
|
141
|
+
*/
|
|
142
|
+
readonly choice?: true;
|
|
137
143
|
}
|
|
138
144
|
/** A button pressed on a card the bot posted. */
|
|
139
145
|
export interface InboundAction extends InboundBase {
|
|
@@ -121,8 +121,8 @@ export interface AssistantRemoteAsk {
|
|
|
121
121
|
/** When the connector stops waiting for an answer (it refuses the request then); absent when it did not say. */
|
|
122
122
|
expires_at?: string;
|
|
123
123
|
/**
|
|
124
|
-
* `terminal`: asked in a terminal on the machine (a conversation its person started there),
|
|
125
|
-
* answer comes first is taken. Absent for a session opened through Claw.
|
|
124
|
+
* `terminal`: asked in a terminal on the machine (a conversation its person started there), which asks it too (when: see
|
|
125
|
+
* `terminal_asks`); whichever answer comes first is taken. Absent for a session opened through Claw.
|
|
126
126
|
*/
|
|
127
127
|
origin?: "terminal";
|
|
128
128
|
/** A terminal's request: what kind of prompt it is (`execute`, `edit`, `question`, `switch_mode` for a plan, `mcp`, `other`). */
|
|
@@ -139,6 +139,22 @@ export interface AssistantRemoteAsk {
|
|
|
139
139
|
/** A terminal's question asked as several cards: which this is (from 0), and how many. */
|
|
140
140
|
question_index?: number;
|
|
141
141
|
question_count?: number;
|
|
142
|
+
/**
|
|
143
|
+
* A terminal's request: when the terminal asks it too, as its connector said (`terminal_asks` on the request). Absent: alongside
|
|
144
|
+
* the card, and the first answer counts (Claude Code). `after_card`: only once the card is answered or its time is up, and the
|
|
145
|
+
* card offers only to decline or to hand it back to the terminal (Cursor). `on_input`: only once someone types in that terminal,
|
|
146
|
+
* which withdraws the card (Codex).
|
|
147
|
+
*/
|
|
148
|
+
terminal_asks?: TerminalAsks;
|
|
149
|
+
}
|
|
150
|
+
/** When a terminal asks a request that is also on a card, besides alongside it: see {@link AssistantRemoteAsk.terminal_asks}. */
|
|
151
|
+
export type TerminalAsks = "after_card" | "on_input";
|
|
152
|
+
/** The values of `terminal_asks` a channel knows; any other is read as absent. */
|
|
153
|
+
export declare const TERMINAL_ASKS: readonly TerminalAsks[];
|
|
154
|
+
/** A choice a say puts to the person: from 2 to 10 options, one plain line each; `multiple` when several may be picked. */
|
|
155
|
+
export interface AssistantSayChoice {
|
|
156
|
+
options: string[];
|
|
157
|
+
multiple: boolean;
|
|
142
158
|
}
|
|
143
159
|
/** Something the assistant chose to say to the user. */
|
|
144
160
|
export interface AssistantSayPayload extends AssistantEventBase {
|
|
@@ -151,6 +167,12 @@ export interface AssistantSayPayload extends AssistantEventBase {
|
|
|
151
167
|
* next, each one plain line. A channel shows them as buttons that send the words as the user's message; one that cannot ignores them.
|
|
152
168
|
*/
|
|
153
169
|
suggested_replies?: string[];
|
|
170
|
+
/**
|
|
171
|
+
* Present only when the router put a choice to the person (`CLAW_ASSISTANT_CHOICES`): the options they pick from, each one plain
|
|
172
|
+
* line, and whether they may pick several. A channel shows them as a card whose Send sends the picked options as the person's own
|
|
173
|
+
* message (their words, joined); one that cannot ignores it, and the text still names the options.
|
|
174
|
+
*/
|
|
175
|
+
choices?: AssistantSayChoice;
|
|
154
176
|
confirm_write?: AssistantConfirmWrite;
|
|
155
177
|
/** Files the user gets with the message (a worker's deliveries, `deliveries.ts`): one download each. */
|
|
156
178
|
files?: AssistantFile[];
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The card of a question a terminal asks: a coding agent its person started in their own terminal wants to run a command, change
|
|
3
3
|
* a file, start on a plan or have a multiple-choice question answered, and the connector's approval hooks put it here too
|
|
4
|
-
* (`remote.ask` with `origin: "terminal"`). The terminal
|
|
4
|
+
* (`remote.ask` with `origin: "terminal"`). The terminal asks too, as the request's `terminal_asks` says (`terminalAsksOf`): alongside
|
|
5
|
+
* the card, the first answer counting (Claude Code); only once the card is answered or its wait is over (Cursor); or once someone
|
|
6
|
+
* types in that terminal, which takes the question back from the card (Codex). The card says which.
|
|
5
7
|
*
|
|
6
8
|
* Nothing is approved unseen. The card shows the request whole (the connector and Claw carry it whole, credentials masked); a
|
|
7
9
|
* request whose content did not come (Claw leaves it out past its size), or that is longer than a card shows, keeps only the
|
|
@@ -9,6 +11,7 @@
|
|
|
9
11
|
* masked in it (a mask can hide more than the credential), or a character the card does not show as itself.
|
|
10
12
|
*/
|
|
11
13
|
import type { Lang } from "../locales/lang.js";
|
|
14
|
+
import { type TerminalAsks } from "../protocol.js";
|
|
12
15
|
import type { DecisionButton } from "../remote/actions.js";
|
|
13
16
|
import { type PermissionOption } from "../remote/options.js";
|
|
14
17
|
import type { Reported } from "../remote/reported.js";
|
|
@@ -62,6 +65,13 @@ export interface TerminalAsk {
|
|
|
62
65
|
readonly machine: string;
|
|
63
66
|
readonly agent: string;
|
|
64
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* When the terminal asks a request that is also on a card (`remote.ask`'s `terminal_asks`, from the connector through Claw):
|
|
70
|
+
* `after_card`, only once the card is answered or its wait is over, and the card offers only to decline or hand it back (Cursor);
|
|
71
|
+
* `on_input`, only once someone types in that terminal, which withdraws the card (Codex); undefined (absent, or a value this
|
|
72
|
+
* client does not know), alongside the card, the first answer counting (Claude Code).
|
|
73
|
+
*/
|
|
74
|
+
export declare function terminalAsksOf(ask: Reported): TerminalAsks | undefined;
|
|
65
75
|
/** Whether `remote.ask` is a question a terminal asks (a connector's approval hooks), not one an agent Claw started asks. */
|
|
66
76
|
export declare function isTerminalAsk(ask: Reported | undefined): boolean;
|
|
67
77
|
/** The card of a question a terminal asks. */
|
|
@@ -19,6 +19,15 @@ export declare function learnLang(store: Pick<RouterStore, "prefs">, userId: str
|
|
|
19
19
|
* "what can you do") and the person did not choose one (`/lang`).
|
|
20
20
|
*/
|
|
21
21
|
export declare function helpLang(store: Pick<RouterStore, "prefs">, userId: string, locale: string, asked: string | undefined): Lang;
|
|
22
|
+
/**
|
|
23
|
+
* The language a group chat is answered in, its fixed texts and its answers alike: the one its members set (`/lang zh|en` there,
|
|
24
|
+
* or a pick on its language card); else (none set, or "follow the asker") the language of the message being answered (`written`).
|
|
25
|
+
* A person's own choice (`/lang` in their 1:1 chat) is not read here: in a group the group decides. A message that says no
|
|
26
|
+
* language of its own ("/new", a link) is answered in the one the sender's messages were last written in, else their client's.
|
|
27
|
+
*/
|
|
28
|
+
export declare function groupLangOf(store: Pick<RouterStore, "prefs"> & Partial<Pick<RouterStore, "groupLanguage">>, conversationId: string, userId: string, locale: string, written: string): Lang;
|
|
29
|
+
/** The language a group chat's members set, when it is one (`/lang zh|en`); undefined for none, and for "follow the asker". */
|
|
30
|
+
export declare function groupChosenLang(store: Partial<Pick<RouterStore, "groupLanguage">>, conversationId: string): Lang | undefined;
|
|
22
31
|
/** One chat as the router answers in it: who wrote, where to answer, and in which language. */
|
|
23
32
|
export declare class Chat {
|
|
24
33
|
private readonly store;
|
|
@@ -28,11 +37,16 @@ export declare class Chat {
|
|
|
28
37
|
readonly scope: string;
|
|
29
38
|
/** The client's locale of the event being handled ("" for work that runs on its own). */
|
|
30
39
|
readonly locale: string;
|
|
31
|
-
|
|
40
|
+
/** The words of the message being answered ("" for a click, and for work that runs on its own): a group's language without one. */
|
|
41
|
+
readonly written: string;
|
|
42
|
+
constructor(store: Pick<RouterStore, "prefs"> & Partial<Pick<RouterStore, "groupLanguage">>, sender: UserRef, conversation: Conversation,
|
|
32
43
|
/** "" for the person's 1:1 chat, the conversation id of a group. */
|
|
33
44
|
scope: string,
|
|
34
45
|
/** The client's locale of the event being handled ("" for work that runs on its own). */
|
|
35
|
-
locale: string
|
|
46
|
+
locale: string,
|
|
47
|
+
/** The words of the message being answered ("" for a click, and for work that runs on its own): a group's language without one. */
|
|
48
|
+
written?: string);
|
|
49
|
+
/** In a 1:1 chat the person's language (`langOf`); in a group, the group's (`groupLangOf`). */
|
|
36
50
|
lang(): Lang;
|
|
37
51
|
zh(): boolean;
|
|
38
52
|
say(text: string): Promise<void>;
|
|
@@ -38,7 +38,7 @@ export interface RouterConfig {
|
|
|
38
38
|
readonly ownThumbsMobile: boolean;
|
|
39
39
|
}
|
|
40
40
|
/** The repositories the router reads and writes (the W1 store, as far as routing goes). */
|
|
41
|
-
export type RouterStore = Pick<StorePort, "links" | "references" | "prefs" | "members" | "sessions" | "repoHistory" | "turns" | "onboarding" | "idempotency" | "assistantMessages" | "notifySubs" | "reviewDelegation" | "approvals" | "now">;
|
|
41
|
+
export type RouterStore = Pick<StorePort, "links" | "references" | "prefs" | "groupLanguage" | "members" | "sessions" | "repoHistory" | "turns" | "onboarding" | "idempotency" | "assistantMessages" | "notifySubs" | "reviewDelegation" | "approvals" | "now">;
|
|
42
42
|
/** The per-person limits (Python `limits.py`): messages per window and tasks at the same time. */
|
|
43
43
|
export interface RouterLimits {
|
|
44
44
|
readonly maxConcurrentTasks: number;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { InboundAction } from "../ports/surface.js";
|
|
2
|
+
import type { Chat } from "./chat.js";
|
|
3
|
+
import type { RouterContext } from "./context.js";
|
|
4
|
+
/** The verb of the language card's buttons. */
|
|
5
|
+
export declare const GROUP_LANG_VERB = "group_lang";
|
|
6
|
+
/**
|
|
7
|
+
* The language a group chat is answered in, as its members set it (the store's `groupLanguage`): `@bot /lang zh|en|auto` or a
|
|
8
|
+
* sentence that names the group ("这个群用英文"), and a card the first time the bot is @mentioned in a chat that has none. Anyone in
|
|
9
|
+
* the chat may set it, with no Claw credential (it is the chat's, not one person's), and every change is announced in the chat with
|
|
10
|
+
* the name of who made it. The group's language then governs everything said there (`Chat.lang`, `groupLangOf`) and the language
|
|
11
|
+
* its turns are asked to answer in (`TurnStarter`).
|
|
12
|
+
*/
|
|
13
|
+
export declare class GroupLanguages {
|
|
14
|
+
private readonly ctx;
|
|
15
|
+
constructor(ctx: RouterContext);
|
|
16
|
+
/** `/lang …` or a sentence that sets the group's language: answered here; false when `text` is neither. */
|
|
17
|
+
command(chat: Chat, text: string): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* The first @mention in a chat that has no language: the card that asks its members which, once (the chat is recorded as asked
|
|
20
|
+
* before the card goes, so two mentions at once put one; a card that could not be posted is asked again next time).
|
|
21
|
+
*/
|
|
22
|
+
askOnce(chat: Chat): Promise<void>;
|
|
23
|
+
/** A pick on the language card, by anyone in the chat: the language is set, the card says so, and so does the chat. */
|
|
24
|
+
click(action: InboundAction, chat: Chat): Promise<void>;
|
|
25
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { InboundAction, InboundMessage } from "../ports/surface.js";
|
|
|
3
3
|
import { type Chat } from "./chat.js";
|
|
4
4
|
import type { SessionCommands } from "./commands-session.js";
|
|
5
5
|
import type { RouterContext } from "./context.js";
|
|
6
|
+
import { type GroupLanguages } from "./group-language.js";
|
|
6
7
|
import type { GroupRemote } from "./group-remote.js";
|
|
7
8
|
import type { Onboarding } from "./onboarding.js";
|
|
8
9
|
import { type PersonalText } from "./personal-text.js";
|
|
@@ -18,7 +19,8 @@ export declare class GroupRouter {
|
|
|
18
19
|
private readonly onboarding;
|
|
19
20
|
private readonly remote;
|
|
20
21
|
private readonly starter;
|
|
21
|
-
|
|
22
|
+
private readonly languages;
|
|
23
|
+
constructor(ctx: RouterContext, text: PersonalText, sessions: SessionCommands, onboarding: Onboarding, remote: GroupRemote, starter: TurnStarter, languages: GroupLanguages);
|
|
22
24
|
route(event: InboundMessage | InboundAction, chat: Chat): Promise<void>;
|
|
23
25
|
/** A button in a group: only those of the presser's own cards that may be pressed there answer. */
|
|
24
26
|
private click;
|
|
@@ -15,7 +15,8 @@ export declare class PersonalText {
|
|
|
15
15
|
constructor(ctx: RouterContext, commands: CommandTable, starter: TurnStarter);
|
|
16
16
|
/**
|
|
17
17
|
* What the person wrote, with a word of the commands menu read as its command and a sentence help teaches read as the
|
|
18
|
-
* command it means; a sentence for what this chat cannot do right now stays words.
|
|
18
|
+
* command it means; a sentence for what this chat cannot do right now stays words. A choice picked on the assistant's card
|
|
19
|
+
* is the words picked, never a command.
|
|
19
20
|
*/
|
|
20
21
|
textOf(message: InboundMessage, group?: boolean): string;
|
|
21
22
|
onText(message: InboundMessage, chat: Chat, key: string): Promise<void>;
|
|
@@ -43,6 +43,12 @@ export interface TurnRun {
|
|
|
43
43
|
readonly sent: string;
|
|
44
44
|
readonly files: readonly InboundFile[];
|
|
45
45
|
readonly lang: Lang;
|
|
46
|
+
/**
|
|
47
|
+
* The language the turn is to be answered in, told to Claw after the message: in a 1:1 chat the one the person chose (`/lang
|
|
48
|
+
* zh|en`, `chosenLang`), in a group the group's (`groupChosenLang`); undefined: none was chosen, and the answer is in the language
|
|
49
|
+
* of what they wrote. A chat turn's only: a review's follows its request.
|
|
50
|
+
*/
|
|
51
|
+
readonly replyLang?: Lang;
|
|
46
52
|
/** The answer gets thumbs of the worker's own (a phone, whose own thumbs never reach the worker). */
|
|
47
53
|
readonly ownThumbs: boolean;
|
|
48
54
|
/** Claw pushes this person a run's end and its approvals. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Lang } from "../locales/lang.js";
|
|
2
2
|
import type { Logger } from "../ports/logger.js";
|
|
3
3
|
import type { InboundFile } from "../ports/surface.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type Chat } from "./chat.js";
|
|
5
5
|
import type { RouterContext } from "./context.js";
|
|
6
6
|
import type { ReviewRunFields, TurnOutcome } from "./turn-runner.js";
|
|
7
7
|
/** Teams on a phone or a tablet: its own Stop under a reply is greyed out, and its own thumbs never reach the worker. */
|
|
@@ -72,7 +72,7 @@ export declare class TurnStarter {
|
|
|
72
72
|
/** The turns that run now (a test waits for them). */
|
|
73
73
|
settled(): Promise<void>;
|
|
74
74
|
/** The turn that runs (its end is `done`), or undefined when none was started (the person was told why). */
|
|
75
|
-
start(
|
|
75
|
+
start(started: StartTurn): Promise<RunningTurn | undefined>;
|
|
76
76
|
/** A typed message the waiting question card of the chat's session took as its answer (`question-reply.ts`). */
|
|
77
77
|
private answersQuestion;
|
|
78
78
|
private track;
|
|
@@ -97,6 +97,11 @@ export declare class TurnStarter {
|
|
|
97
97
|
/** What the run reads of the chat's session: its id, the model it has, the repository a new one is bound to. */
|
|
98
98
|
private sessionPart;
|
|
99
99
|
private runOf;
|
|
100
|
+
/**
|
|
101
|
+
* The language a chat turn is to be answered in, when one was chosen (a review's answer follows its request's language): in a
|
|
102
|
+
* group the group's (`groupChosenLang`; the sender's own choice is not read there), in a 1:1 chat the person's.
|
|
103
|
+
*/
|
|
104
|
+
private replyLang;
|
|
100
105
|
private hooks;
|
|
101
106
|
private guarded;
|
|
102
107
|
private tell;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Everything one turn runs with: who it acts for, what it sends, where it answers, and what it reports back. */
|
|
2
1
|
import type { Lang } from "../../locales/lang.js";
|
|
3
2
|
import type { PullRef } from "../../claw/model.js";
|
|
4
3
|
import type { Integrations } from "../../plugins/integrations.js";
|
|
@@ -60,6 +59,8 @@ export interface TurnRequest {
|
|
|
60
59
|
readonly sent?: string;
|
|
61
60
|
/** The language the person is answered in (undefined: the language of `text`). */
|
|
62
61
|
readonly lang?: Lang;
|
|
62
|
+
/** The language the person chose to be answered in, told to Claw after the message (`replyLanguageLine`); undefined: none chosen. */
|
|
63
|
+
readonly replyLang?: Lang;
|
|
63
64
|
/** What the user sent with the message. */
|
|
64
65
|
readonly files?: readonly InboundFile[];
|
|
65
66
|
}
|
|
@@ -154,6 +155,7 @@ export declare function credential(ctx: TurnContext): Promise<string>;
|
|
|
154
155
|
export declare function langOf(ctx: TurnContext): Lang;
|
|
155
156
|
/**
|
|
156
157
|
* What Claw is sent: the quoted messages, then what the user wrote (with the addresses behind its links), then where the
|
|
157
|
-
* files they sent are
|
|
158
|
+
* files they sent are, then the language they chose to be answered in (`replyLanguageLine`), when they chose one. `text` stays the
|
|
159
|
+
* words alone: the session's title, the language of the answer.
|
|
158
160
|
*/
|
|
159
161
|
export declare function messageOf(ctx: TurnContext): string;
|