@primusclaw/personal-agent 0.6.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 +296 -35
- package/dist/types/language/chosen.d.ts +3 -2
- package/dist/types/language/group.d.ts +18 -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/group-language.d.ts +20 -0
- package/dist/types/ports/store/index.d.ts +3 -0
- package/dist/types/protocol/claw-api/assistant-events.d.ts +13 -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/turn-runner.d.ts +3 -2
- package/dist/types/routing/turn-start.d.ts +5 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4990,6 +4990,7 @@ var ITEM_KINDS = ["task", "review", "remote"];
|
|
|
4990
4990
|
|
|
4991
4991
|
// ../claw/packages/protocol/src/claw-api/assistant-events.ts
|
|
4992
4992
|
var ASSISTANT_EVENT_TYPES = ["assistant.say", "work_item.needs_you", "assistant.action"];
|
|
4993
|
+
var TERMINAL_ASKS = ["after_card", "on_input"];
|
|
4993
4994
|
var REVIEW_HANDOFF_ACTION = "review_for_other";
|
|
4994
4995
|
|
|
4995
4996
|
// ../claw/packages/protocol/src/claw-api/notifications.ts
|
|
@@ -6137,8 +6138,8 @@ function writtenLang(text8) {
|
|
|
6137
6138
|
const words3 = written3.map((w) => w.toLowerCase());
|
|
6138
6139
|
if (words3.length < 3) return void 0;
|
|
6139
6140
|
const first = words3[0] ?? "";
|
|
6140
|
-
const
|
|
6141
|
-
return
|
|
6141
|
+
const sentence3 = EN_VERBS.has(first) || words3.some((w) => EN_GLUE.has(w)) || written3.some((w) => EN_FORM.test(w));
|
|
6142
|
+
return sentence3 ? "en" : void 0;
|
|
6142
6143
|
}
|
|
6143
6144
|
function pickLang2(...texts4) {
|
|
6144
6145
|
for (const text8 of texts4) {
|
|
@@ -9557,21 +9558,34 @@ function helpLang(store, userId, locale, asked3) {
|
|
|
9557
9558
|
const written3 = asked3 !== void 0 && !prefs.langSet ? writtenLang(asked3) : void 0;
|
|
9558
9559
|
return written3 ?? langOf2(store, userId, locale);
|
|
9559
9560
|
}
|
|
9561
|
+
function groupLangOf(store, conversationId, userId, locale, written3) {
|
|
9562
|
+
const chosen = groupChosenLang(store, conversationId);
|
|
9563
|
+
if (chosen !== void 0) return chosen;
|
|
9564
|
+
const prefs = userId ? store.prefs.getPrefs(userId) : void 0;
|
|
9565
|
+
return writtenLang(written3) ?? answerLang(prefs !== void 0 && !prefs.langSet ? prefs.lang : "", locale);
|
|
9566
|
+
}
|
|
9567
|
+
function groupChosenLang(store, conversationId) {
|
|
9568
|
+
const lang = conversationId ? store.groupLanguage?.groupLanguage(conversationId) : null;
|
|
9569
|
+
return lang === "zh" || lang === "en" ? lang : void 0;
|
|
9570
|
+
}
|
|
9560
9571
|
var Chat = class {
|
|
9561
|
-
constructor(store, sender, conversation, scope, locale) {
|
|
9572
|
+
constructor(store, sender, conversation, scope, locale, written3 = "") {
|
|
9562
9573
|
this.store = store;
|
|
9563
9574
|
this.sender = sender;
|
|
9564
9575
|
this.conversation = conversation;
|
|
9565
9576
|
this.scope = scope;
|
|
9566
9577
|
this.locale = locale;
|
|
9578
|
+
this.written = written3;
|
|
9567
9579
|
}
|
|
9568
9580
|
store;
|
|
9569
9581
|
sender;
|
|
9570
9582
|
conversation;
|
|
9571
9583
|
scope;
|
|
9572
9584
|
locale;
|
|
9585
|
+
written;
|
|
9586
|
+
/** In a 1:1 chat the person's language (`langOf`); in a group, the group's (`groupLangOf`). */
|
|
9573
9587
|
lang() {
|
|
9574
|
-
return langOf2(this.store, this.sender.id, this.locale);
|
|
9588
|
+
return this.scope ? groupLangOf(this.store, this.scope, this.sender.id, this.locale, this.written) : langOf2(this.store, this.sender.id, this.locale);
|
|
9575
9589
|
}
|
|
9576
9590
|
zh() {
|
|
9577
9591
|
return this.lang() === "zh";
|
|
@@ -11017,7 +11031,12 @@ function renderHelp(language, options = {}) {
|
|
|
11017
11031
|
const oneToOne = chat.group ? oneToOneBlock(lang, chat, bot) : void 0;
|
|
11018
11032
|
if (oneToOne !== void 0) out.push(oneToOne);
|
|
11019
11033
|
out.push(authorityText(zh, chat.parts));
|
|
11020
|
-
if (chat.group)
|
|
11034
|
+
if (chat.group)
|
|
11035
|
+
out.push(
|
|
11036
|
+
zh ? `\u8BBE\u7F6E\u53EA\u80FD\u5728\u548C\u6211\u7684 1:1 \u804A\u5929\u91CC\u6539\u3002
|
|
11037
|
+
\u53EA\u6709\u8FD9\u4E2A\u7FA4\u7684\u56DE\u590D\u8BED\u8A00\u5728\u7FA4\u91CC\u8BBE\uFF1A@${bot} \u8BF4\u300C\u8FD9\u4E2A\u7FA4\u7528\u82F1\u6587\u300D\uFF08\u6216\u4E2D\u6587\u3001\u8DDF\u968F\u63D0\u95EE\u8005\uFF09\u3002` : `Settings are changed in a 1:1 chat with me.
|
|
11038
|
+
This group's language is set here: @${bot} "use English in this group" (or Chinese, or "follow the asker").`
|
|
11039
|
+
);
|
|
11021
11040
|
out.push(dataStatement(lang, !chat.group && chat.parts.has("remote"), options.groupContext));
|
|
11022
11041
|
out.push(
|
|
11023
11042
|
zh ? "\u968F\u65F6\u95EE\u6211\u300C\u4F60\u80FD\u505A\u4EC0\u4E48\u300D\u770B\u8FD9\u4EFD\u8BF4\u660E\u3002\u5E95\u90E8\u83DC\u5355\u91CC\u4E5F\u6709\u5E38\u7528\u7684\u6309\u94AE\u3002" : 'Ask "what can you do" any time to see this again. The menu by the compose box has the common ones too.'
|
|
@@ -11600,8 +11619,8 @@ var VALUED = [
|
|
|
11600
11619
|
function personal(text8, flat2, key) {
|
|
11601
11620
|
if (STOP_IT2.has(key)) return "/stop";
|
|
11602
11621
|
if (DETACH2.has(detachKey(key))) return "/agent detach";
|
|
11603
|
-
const
|
|
11604
|
-
if (
|
|
11622
|
+
const phrase2 = lookUp(key, PERSONAL_PHRASES);
|
|
11623
|
+
if (phrase2 !== null) return phrase2;
|
|
11605
11624
|
for (const read of VALUED) {
|
|
11606
11625
|
const command = read(flat2);
|
|
11607
11626
|
if (command !== null) return command;
|
|
@@ -12095,11 +12114,13 @@ var RouterContext = class {
|
|
|
12095
12114
|
/** The chat an event is answered in: its conversation, as the person who sent it. */
|
|
12096
12115
|
chatOf(event, scope) {
|
|
12097
12116
|
const conversation = this.deps.surface.conversation(event.conversation);
|
|
12098
|
-
|
|
12117
|
+
const written3 = event.type === "message" ? event.text : "";
|
|
12118
|
+
return new Chat(this.deps.store, event.sender, conversation, scope, event.locale, written3);
|
|
12099
12119
|
}
|
|
12100
12120
|
/** A chat in another conversation of the same person (their 1:1 chat, a group a request waited in). */
|
|
12101
12121
|
chatIn(chat, ref, scope = "") {
|
|
12102
|
-
|
|
12122
|
+
const written3 = scope ? chat.written : "";
|
|
12123
|
+
return new Chat(this.deps.store, chat.sender, this.deps.surface.conversation(ref), scope, chat.locale, written3);
|
|
12103
12124
|
}
|
|
12104
12125
|
/** A click as a feature handler is given it. */
|
|
12105
12126
|
clickRoute(action, chat) {
|
|
@@ -12152,8 +12173,8 @@ var groupText = defineCatalog({
|
|
|
12152
12173
|
stopMany: (p) => `#${String(p.number)}: more than one repository has it; write it as owner/repo#${String(p.number)}.`
|
|
12153
12174
|
},
|
|
12154
12175
|
zh: {
|
|
12155
|
-
githubOneToOne: "`/github`
|
|
12156
|
-
oneToOneOnly: (p) => `\`${p.word}\`
|
|
12176
|
+
githubOneToOne: "`/github` \u53EA\u80FD\u5728 1:1 \u804A\u5929\u91CC\u7528\uFF0C\u8BF7\u5728\u548C\u6211\u7684 1:1 \u804A\u5929\u91CC\u64CD\u4F5C\u3002",
|
|
12177
|
+
oneToOneOnly: (p) => `\`${p.word}\` \u53EA\u80FD\u5728 1:1 \u804A\u5929\u91CC\u7528\uFF0C\u8BF7\u5728\u548C\u6211\u7684 1:1 \u804A\u5929\u91CC\u64CD\u4F5C\u3002`,
|
|
12157
12178
|
agentInGroup: "`/agent` \u5728\u7FA4\u91CC\u53EA\u80FD\u7528 attach \u548C detach\uFF0C\u5176\u4F59\u8BF7\u5728 1:1 \u804A\u5929\u91CC\u7528\u3002",
|
|
12158
12179
|
whoami: (p) => `${p.name || "\u4F60"} \u5DF2\u8FDE\u63A5\u5230 SaFE \u7528\u6237 ${p.who}\u3002`,
|
|
12159
12180
|
whoamiUnknown: "\u4F60\u5DF2\u8FDE\u63A5\uFF0C\u4F46 SaFE \u6CA1\u544A\u8BC9\u6211\u662F\u8C01\u7684\u8D26\u53F7\u3002",
|
|
@@ -12319,7 +12340,8 @@ var Onboarding = class {
|
|
|
12319
12340
|
try {
|
|
12320
12341
|
const conversation = await this.openPersonal(chat);
|
|
12321
12342
|
if (conversation === void 0) throw new Error("no 1:1 conversation");
|
|
12322
|
-
|
|
12343
|
+
const dm = this.ctx.chatIn(chat, conversation.ref);
|
|
12344
|
+
await dm.say(groupText.in(dm.lang()).targeted({ bot: config.botName }));
|
|
12323
12345
|
} catch (error) {
|
|
12324
12346
|
logger.warn("group: the 1:1 line about a private message did not go out", { error: errorName(error) });
|
|
12325
12347
|
return;
|
|
@@ -13070,6 +13092,208 @@ var SettingsCommands = class {
|
|
|
13070
13092
|
}
|
|
13071
13093
|
};
|
|
13072
13094
|
|
|
13095
|
+
// packages/core/src/language/group.ts
|
|
13096
|
+
var WORDS = /* @__PURE__ */ new Map([
|
|
13097
|
+
["zh", "zh"],
|
|
13098
|
+
["cn", "zh"],
|
|
13099
|
+
["chinese", "zh"],
|
|
13100
|
+
["\u4E2D\u6587", "zh"],
|
|
13101
|
+
["\u6C49\u8BED", "zh"],
|
|
13102
|
+
["en", "en"],
|
|
13103
|
+
["english", "en"],
|
|
13104
|
+
["\u82F1\u6587", "en"],
|
|
13105
|
+
["\u82F1\u8BED", "en"],
|
|
13106
|
+
["auto", "auto"],
|
|
13107
|
+
["follow", "auto"],
|
|
13108
|
+
["\u81EA\u52A8", "auto"],
|
|
13109
|
+
["\u8DDF\u968F", "auto"],
|
|
13110
|
+
["\u8DDF\u968F\u63D0\u95EE\u8005", "auto"],
|
|
13111
|
+
["followtheasker", "auto"]
|
|
13112
|
+
]);
|
|
13113
|
+
var PUNCT2 = ` \r
|
|
13114
|
+
\u3002\uFF0E.\uFF01!\uFF1F?\uFF5E~\uFF0C,\u3001\uFF1B;\uFF1A:"'\u201C\u201D\u2018\u2019\uFF08\uFF09()`;
|
|
13115
|
+
var GROUP = "(?:\u8FD9\u4E2A\u7FA4\u804A|\u8FD9\u4E2A\u7FA4|\u672C\u7FA4|\u6B64\u7FA4|\u7FA4\u804A|\u7FA4)(?:\u91CC\u9762|\u91CC|\u5185|\u4E2D)?";
|
|
13116
|
+
var LANG_ZH = "(\u4E2D\u6587|\u6C49\u8BED|\u82F1\u6587|\u82F1\u8BED)";
|
|
13117
|
+
var ASKER = "(?:\u63D0\u95EE\u8005|\u63D0\u95EE\u4EBA|\u63D0\u95EE\u7684\u4EBA|\u95EE\u95EE\u9898\u7684\u4EBA|\u95EE\u7684\u4EBA|\u53D1\u95EE\u7684\u4EBA)";
|
|
13118
|
+
var ZH_SET = [
|
|
13119
|
+
new RegExp(
|
|
13120
|
+
`^(?:\u4EE5\u540E|\u4E4B\u540E|\u4ECA\u540E|\u4ECE\u73B0\u5728\u8D77|\u4ECE\u73B0\u5728\u5F00\u59CB)?(?:\u8BF7|\u9EBB\u70E6)?(?:\u5728)?${GROUP}(?:\u4EE5\u540E|\u4E4B\u540E|\u4ECA\u540E|\u4ECE\u73B0\u5728\u8D77|\u4ECE\u73B0\u5728\u5F00\u59CB)?(?:\u8BF7)?(?:\u90FD|\u4E00\u5F8B|\u7EDF\u4E00)?(?:\u7528|\u8BF4|\u4F7F\u7528)${LANG_ZH}(?:\u56DE\u590D|\u56DE\u7B54|\u4EA4\u6D41|\u8BF4\u8BDD|\u804A\u5929|\u804A)?(?:\u5427|\u597D\u5417|\u53EF\u4EE5\u5417)?$`,
|
|
13121
|
+
"u"
|
|
13122
|
+
),
|
|
13123
|
+
new RegExp(
|
|
13124
|
+
`^(?:\u628A)?${GROUP}(?:\u7684)?(?:\u56DE\u590D)?\u8BED\u8A00(?:\u8BBE\u4E3A|\u8BBE\u6210|\u6539\u6210|\u6539\u4E3A|\u8BBE\u7F6E\u4E3A|\u8BBE\u7F6E\u6210|\u5207\u6362\u5230|\u5207\u6362\u6210|\u6362\u6210)${LANG_ZH}$`,
|
|
13125
|
+
"u"
|
|
13126
|
+
)
|
|
13127
|
+
];
|
|
13128
|
+
var ZH_AUTO = [
|
|
13129
|
+
new RegExp(
|
|
13130
|
+
`^(?:\u4EE5\u540E|\u4E4B\u540E|\u4ECA\u540E)?(?:\u5728)?${GROUP}(?:\u4EE5\u540E|\u4E4B\u540E|\u4ECA\u540E)?(?:\u90FD)?(?:\u8DDF\u968F|\u8DDF\u7740|\u6309|\u6309\u7167|\u7528)${ASKER}(?:\u7684)?(?:\u8BED\u8A00)?(?:\u56DE\u590D|\u56DE\u7B54)?(?:\u5427)?$`,
|
|
13131
|
+
"u"
|
|
13132
|
+
),
|
|
13133
|
+
new RegExp(
|
|
13134
|
+
`^(?:\u628A)?${GROUP}(?:\u7684)?(?:\u56DE\u590D)?\u8BED\u8A00(?:\u8BBE\u4E3A|\u8BBE\u6210|\u6539\u6210|\u6539\u4E3A|\u8BBE\u7F6E\u4E3A|\u8BBE\u7F6E\u6210|\u5207\u6362\u5230|\u5207\u6362\u6210|\u6362\u6210)(?:\u81EA\u52A8|\u8DDF\u968F${ASKER})$`,
|
|
13135
|
+
"u"
|
|
13136
|
+
)
|
|
13137
|
+
];
|
|
13138
|
+
var LANG_EN = "(english|chinese)";
|
|
13139
|
+
var THIS_GROUP = "(?:this|the|our) (?:group chat|group|chat)";
|
|
13140
|
+
var ANSWER = "(?:use|speak|reply in|answer in|respond in|talk in|write in)";
|
|
13141
|
+
var EN_SET = [
|
|
13142
|
+
new RegExp(`^(?:please )?${ANSWER} ${LANG_EN} (?:in|for) ${THIS_GROUP}(?: from now on)?(?: please)?$`),
|
|
13143
|
+
new RegExp(`^(?:in|for) ${THIS_GROUP},? (?:please )?${ANSWER} ${LANG_EN}(?: from now on)?(?: please)?$`),
|
|
13144
|
+
new RegExp(`^${THIS_GROUP} (?:uses|speaks|is in) ${LANG_EN}$`),
|
|
13145
|
+
new RegExp(
|
|
13146
|
+
`^(?:please )?(?:set|change|switch) (?:the |this )?(?:group|chat)(?:'s)? language to ${LANG_EN}(?: please)?$`
|
|
13147
|
+
)
|
|
13148
|
+
];
|
|
13149
|
+
var EN_AUTO = [
|
|
13150
|
+
new RegExp(
|
|
13151
|
+
`^(?:please )?(?:follow the asker|use the asker'?s language|reply in the asker'?s language|answer in the language of each question)(?: (?:in|for) ${THIS_GROUP})?(?: please)?$`
|
|
13152
|
+
),
|
|
13153
|
+
new RegExp(
|
|
13154
|
+
`^(?:please )?(?:set|change|switch) (?:the |this )?(?:group|chat)(?:'s)? language to (?:auto|follow the asker)(?: please)?$`
|
|
13155
|
+
)
|
|
13156
|
+
];
|
|
13157
|
+
function phrase(text8) {
|
|
13158
|
+
return pySplit(pyLower(pyStrip(text8, PUNCT2))).join("");
|
|
13159
|
+
}
|
|
13160
|
+
function sentence(text8) {
|
|
13161
|
+
return pySplit(pyLower(pyStrip(text8, PUNCT2))).join(" ");
|
|
13162
|
+
}
|
|
13163
|
+
var MAX_SENTENCE_CHARS = 120;
|
|
13164
|
+
function sentenceCommand(text8) {
|
|
13165
|
+
if (text8.length > MAX_SENTENCE_CHARS) return void 0;
|
|
13166
|
+
const zh = phrase(text8);
|
|
13167
|
+
const en2 = sentence(text8);
|
|
13168
|
+
for (const pattern of ZH_SET) {
|
|
13169
|
+
const word2 = pattern.exec(zh)?.[1];
|
|
13170
|
+
const lang = word2 === void 0 ? void 0 : WORDS.get(word2);
|
|
13171
|
+
if (lang !== void 0) return { kind: "set", lang };
|
|
13172
|
+
}
|
|
13173
|
+
if (ZH_AUTO.some((p) => p.test(zh))) return { kind: "set", lang: "auto" };
|
|
13174
|
+
for (const pattern of EN_SET) {
|
|
13175
|
+
const word2 = pattern.exec(en2)?.[1];
|
|
13176
|
+
const lang = word2 === void 0 ? void 0 : WORDS.get(word2);
|
|
13177
|
+
if (lang !== void 0) return { kind: "set", lang };
|
|
13178
|
+
}
|
|
13179
|
+
if (EN_AUTO.some((p) => p.test(en2))) return { kind: "set", lang: "auto" };
|
|
13180
|
+
return void 0;
|
|
13181
|
+
}
|
|
13182
|
+
function parseGroupLang(text8) {
|
|
13183
|
+
const words3 = pySplit(text8);
|
|
13184
|
+
if (pyLower(words3[0] ?? "") !== "/lang") return sentenceCommand(pyStrip(text8));
|
|
13185
|
+
if (words3.length === 1) return { kind: "status" };
|
|
13186
|
+
const lang = WORDS.get(pyLower(words3.slice(1).join("")));
|
|
13187
|
+
return lang === void 0 ? { kind: "usage" } : { kind: "set", lang };
|
|
13188
|
+
}
|
|
13189
|
+
|
|
13190
|
+
// packages/core/src/locales/group-language.ts
|
|
13191
|
+
var NAMES2 = {
|
|
13192
|
+
zh: ["\u4E2D\u6587", "Chinese"],
|
|
13193
|
+
en: ["\u82F1\u6587", "English"],
|
|
13194
|
+
auto: ["\u8DDF\u968F\u63D0\u95EE\u8005\u7684\u8BED\u8A00", "the language each question is asked in"]
|
|
13195
|
+
};
|
|
13196
|
+
function groupLanguageSet(lang, name) {
|
|
13197
|
+
const [zh, en2] = NAMES2[lang];
|
|
13198
|
+
return `\u5DF2\u6539\u4E3A${zh}\u56DE\u590D\uFF0C\u7531 ${name || "\u7FA4\u6210\u5458"} \u8BBE\u7F6E\u3002
|
|
13199
|
+
I answer in ${en2} in this group from now on (set by ${name || "a member"}).`;
|
|
13200
|
+
}
|
|
13201
|
+
var GROUP_LANGUAGE_CARD = {
|
|
13202
|
+
title: "\u8FD9\u4E2A\u7FA4\u7528\u54EA\u79CD\u8BED\u8A00\u56DE\u590D\uFF1F/ Which language should I use in this group?",
|
|
13203
|
+
text: (p) => `\u7FA4\u91CC\u4EFB\u4F55\u4EBA\u90FD\u53EF\u4EE5\u9009\uFF0C\u4E4B\u540E\u4E5F\u80FD\u7528 @${p.bot} /lang zh | en | auto \u518D\u6539\u3002
|
|
13204
|
+
Anyone here may pick; @${p.bot} /lang zh | en | auto changes it later.`,
|
|
13205
|
+
buttons: { zh: "\u4E2D\u6587", en: "English", auto: "\u8DDF\u968F\u63D0\u95EE\u8005 (Follow the asker)" }
|
|
13206
|
+
};
|
|
13207
|
+
var groupLanguageText = defineCatalog({
|
|
13208
|
+
en: {
|
|
13209
|
+
isZh: (p) => `This group is answered in Chinese. @${p.bot} /lang en or /lang auto changes it.`,
|
|
13210
|
+
isEn: (p) => `This group is answered in English. @${p.bot} /lang zh or /lang auto changes it.`,
|
|
13211
|
+
isAuto: (p) => `This group is answered in the language each question is asked in. @${p.bot} /lang zh or /lang en sets one.`,
|
|
13212
|
+
isNone: (p) => `This group has no language set: each question is answered in the language it is asked in. @${p.bot} /lang zh, /lang en or /lang auto sets it.`,
|
|
13213
|
+
usage: (p) => `Usage: @${p.bot} /lang zh | en | auto (follow the asker).`
|
|
13214
|
+
},
|
|
13215
|
+
zh: {
|
|
13216
|
+
isZh: (p) => `\u8FD9\u4E2A\u7FA4\u7528\u4E2D\u6587\u56DE\u590D\u3002@${p.bot} /lang en \u6216 /lang auto \u53EF\u6539\u3002`,
|
|
13217
|
+
isEn: (p) => `\u8FD9\u4E2A\u7FA4\u7528\u82F1\u6587\u56DE\u590D\u3002@${p.bot} /lang zh \u6216 /lang auto \u53EF\u6539\u3002`,
|
|
13218
|
+
isAuto: (p) => `\u8FD9\u4E2A\u7FA4\u6309\u6BCF\u4E2A\u95EE\u9898\u6240\u7528\u7684\u8BED\u8A00\u56DE\u590D\u3002@${p.bot} /lang zh \u6216 /lang en \u53EF\u8BBE\u4E3A\u56FA\u5B9A\u8BED\u8A00\u3002`,
|
|
13219
|
+
isNone: (p) => `\u8FD9\u4E2A\u7FA4\u8FD8\u6CA1\u8BBE\u8BED\u8A00\uFF1A\u6BCF\u4E2A\u95EE\u9898\u6309\u5B83\u6240\u7528\u7684\u8BED\u8A00\u56DE\u590D\u3002@${p.bot} /lang zh\u3001/lang en \u6216 /lang auto \u53EF\u8BBE\u7F6E\u3002`,
|
|
13220
|
+
usage: (p) => `\u7528\u6CD5\uFF1A@${p.bot} /lang zh | en | auto\uFF08\u8DDF\u968F\u63D0\u95EE\u8005\uFF09\u3002`
|
|
13221
|
+
}
|
|
13222
|
+
});
|
|
13223
|
+
|
|
13224
|
+
// packages/core/src/routing/group-language.ts
|
|
13225
|
+
var GROUP_LANG_VERB = "group_lang";
|
|
13226
|
+
var LANGS2 = ["zh", "en", "auto"];
|
|
13227
|
+
function isGroupLanguage(value) {
|
|
13228
|
+
return typeof value === "string" && LANGS2.includes(value);
|
|
13229
|
+
}
|
|
13230
|
+
var GroupLanguages = class {
|
|
13231
|
+
constructor(ctx) {
|
|
13232
|
+
this.ctx = ctx;
|
|
13233
|
+
}
|
|
13234
|
+
ctx;
|
|
13235
|
+
/** `/lang …` or a sentence that sets the group's language: answered here; false when `text` is neither. */
|
|
13236
|
+
async command(chat, text8) {
|
|
13237
|
+
const command = parseGroupLang(text8);
|
|
13238
|
+
if (command === void 0) return false;
|
|
13239
|
+
const bot = this.ctx.deps.config.botName;
|
|
13240
|
+
if (command.kind === "set") {
|
|
13241
|
+
this.ctx.deps.store.groupLanguage.setGroupLanguage(chat.scope, command.lang);
|
|
13242
|
+
await chat.say(groupLanguageSet(command.lang, chat.sender.name));
|
|
13243
|
+
return true;
|
|
13244
|
+
}
|
|
13245
|
+
const said2 = groupLanguageText.in(chat.lang());
|
|
13246
|
+
if (command.kind === "usage") {
|
|
13247
|
+
await chat.say(said2.usage({ bot }));
|
|
13248
|
+
return true;
|
|
13249
|
+
}
|
|
13250
|
+
const lang = this.ctx.deps.store.groupLanguage.groupLanguage(chat.scope);
|
|
13251
|
+
const state = lang === "zh" ? said2.isZh : lang === "en" ? said2.isEn : lang === "auto" ? said2.isAuto : said2.isNone;
|
|
13252
|
+
await chat.say(state({ bot }));
|
|
13253
|
+
return true;
|
|
13254
|
+
}
|
|
13255
|
+
/**
|
|
13256
|
+
* The first @mention in a chat that has no language: the card that asks its members which, once (the chat is recorded as asked
|
|
13257
|
+
* before the card goes, so two mentions at once put one; a card that could not be posted is asked again next time).
|
|
13258
|
+
*/
|
|
13259
|
+
async askOnce(chat) {
|
|
13260
|
+
const { store, logger, config } = this.ctx.deps;
|
|
13261
|
+
if (!store.groupLanguage.markGroupLanguageAsked(chat.scope)) return;
|
|
13262
|
+
const buttons2 = LANGS2.map(
|
|
13263
|
+
(lang) => viewButtonOf({ title: GROUP_LANGUAGE_CARD.buttons[lang], verb: GROUP_LANG_VERB, data: { lang } })
|
|
13264
|
+
);
|
|
13265
|
+
try {
|
|
13266
|
+
await chat.send(
|
|
13267
|
+
noteMessage({
|
|
13268
|
+
title: GROUP_LANGUAGE_CARD.title,
|
|
13269
|
+
text: GROUP_LANGUAGE_CARD.text({ bot: config.botName }),
|
|
13270
|
+
tone: "accent",
|
|
13271
|
+
buttons: buttons2
|
|
13272
|
+
})
|
|
13273
|
+
);
|
|
13274
|
+
} catch (error) {
|
|
13275
|
+
store.groupLanguage.unmarkGroupLanguageAsked(chat.scope);
|
|
13276
|
+
logger.warn("group: the language card could not be posted; it is asked again next time", {
|
|
13277
|
+
error: errorName(error)
|
|
13278
|
+
});
|
|
13279
|
+
}
|
|
13280
|
+
}
|
|
13281
|
+
/** A pick on the language card, by anyone in the chat: the language is set, the card says so, and so does the chat. */
|
|
13282
|
+
async click(action, chat) {
|
|
13283
|
+
const lang = action.data["lang"];
|
|
13284
|
+
if (!isGroupLanguage(lang)) {
|
|
13285
|
+
await this.ctx.settle(
|
|
13286
|
+
action,
|
|
13287
|
+
chat,
|
|
13288
|
+
groupLanguageText.in(chat.lang()).usage({ bot: this.ctx.deps.config.botName })
|
|
13289
|
+
);
|
|
13290
|
+
return;
|
|
13291
|
+
}
|
|
13292
|
+
this.ctx.deps.store.groupLanguage.setGroupLanguage(chat.scope, lang);
|
|
13293
|
+
await this.ctx.settle(action, chat, groupLanguageSet(lang, chat.sender.name));
|
|
13294
|
+
}
|
|
13295
|
+
};
|
|
13296
|
+
|
|
13073
13297
|
// packages/core/src/routing/show-here.ts
|
|
13074
13298
|
var ShowHere = class {
|
|
13075
13299
|
constructor(ctx) {
|
|
@@ -13827,10 +14051,14 @@ var TurnStarter = class {
|
|
|
13827
14051
|
review: review?.fields
|
|
13828
14052
|
};
|
|
13829
14053
|
}
|
|
13830
|
-
/**
|
|
14054
|
+
/**
|
|
14055
|
+
* 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
|
|
14056
|
+
* group the group's (`groupChosenLang`; the sender's own choice is not read there), in a 1:1 chat the person's.
|
|
14057
|
+
*/
|
|
13831
14058
|
replyLang(request) {
|
|
13832
14059
|
if (request.review !== void 0) return {};
|
|
13833
|
-
const
|
|
14060
|
+
const { store } = this.ctx.deps;
|
|
14061
|
+
const lang = request.chat.scope ? groupChosenLang(store, request.chat.scope) : chosenLang(store.prefs.getPrefs(request.chat.sender.id));
|
|
13834
14062
|
return lang === void 0 ? {} : { replyLang: lang };
|
|
13835
14063
|
}
|
|
13836
14064
|
hooks(request, run, turn, turnId) {
|
|
@@ -14176,13 +14404,14 @@ var STOP_SAID = {
|
|
|
14176
14404
|
item_closed: ["\u5DF2\u7ECF\u7ED3\u675F\u4E86\u3002", "it had already ended."]
|
|
14177
14405
|
};
|
|
14178
14406
|
var GroupRouter = class {
|
|
14179
|
-
constructor(ctx, text8, sessions, onboarding, remote, starter) {
|
|
14407
|
+
constructor(ctx, text8, sessions, onboarding, remote, starter, languages) {
|
|
14180
14408
|
this.ctx = ctx;
|
|
14181
14409
|
this.text = text8;
|
|
14182
14410
|
this.sessions = sessions;
|
|
14183
14411
|
this.onboarding = onboarding;
|
|
14184
14412
|
this.remote = remote;
|
|
14185
14413
|
this.starter = starter;
|
|
14414
|
+
this.languages = languages;
|
|
14186
14415
|
}
|
|
14187
14416
|
ctx;
|
|
14188
14417
|
text;
|
|
@@ -14190,6 +14419,7 @@ var GroupRouter = class {
|
|
|
14190
14419
|
onboarding;
|
|
14191
14420
|
remote;
|
|
14192
14421
|
starter;
|
|
14422
|
+
languages;
|
|
14193
14423
|
async route(event, chat) {
|
|
14194
14424
|
if (!chat.scope) return;
|
|
14195
14425
|
if (event.type === "action") await this.click(event, chat);
|
|
@@ -14203,6 +14433,10 @@ var GroupRouter = class {
|
|
|
14203
14433
|
await this.onboarding.click(action, chat);
|
|
14204
14434
|
return;
|
|
14205
14435
|
}
|
|
14436
|
+
if (action.verb === GROUP_LANG_VERB) {
|
|
14437
|
+
await this.languages.click(action, chat);
|
|
14438
|
+
return;
|
|
14439
|
+
}
|
|
14206
14440
|
const handler = this.ctx.deps.handlers.clicks.find((h) => h.verbs.includes(action.verb));
|
|
14207
14441
|
if (handler?.inGroup !== void 0) await handler.inGroup(this.ctx.clickRoute(action, chat));
|
|
14208
14442
|
}
|
|
@@ -14215,6 +14449,9 @@ var GroupRouter = class {
|
|
|
14215
14449
|
const words3 = this.words(message);
|
|
14216
14450
|
learnLang(this.ctx.deps.store, chat.sender.id, message.text);
|
|
14217
14451
|
const choice = message.choice === true;
|
|
14452
|
+
const language = !choice && await this.languages.command(chat, words3.text);
|
|
14453
|
+
await this.languages.askOnce(chat);
|
|
14454
|
+
if (language) return;
|
|
14218
14455
|
if (!choice && await this.groupOwn(message, chat, words3)) return;
|
|
14219
14456
|
const route = {
|
|
14220
14457
|
sender: chat.sender,
|
|
@@ -14244,7 +14481,7 @@ var GroupRouter = class {
|
|
|
14244
14481
|
const [first = ""] = pySplit(words3.text);
|
|
14245
14482
|
const word2 = pyLower(first);
|
|
14246
14483
|
if (empty || word2 === "/help") {
|
|
14247
|
-
const lang =
|
|
14484
|
+
const lang = chat.lang();
|
|
14248
14485
|
const assistant = this.ctx.deps.assistant.on(chat.sender.id);
|
|
14249
14486
|
const groupContext = this.ctx.deps.groupHistory.kept(message.conversation.id);
|
|
14250
14487
|
await chat.say(
|
|
@@ -14259,11 +14496,11 @@ var GroupRouter = class {
|
|
|
14259
14496
|
return true;
|
|
14260
14497
|
}
|
|
14261
14498
|
if (github !== void 0 && word2 === "/github") {
|
|
14262
|
-
await chat.say(groupText.in(
|
|
14499
|
+
await chat.say(groupText.in(chat.lang()).githubOneToOne);
|
|
14263
14500
|
return true;
|
|
14264
14501
|
}
|
|
14265
14502
|
if (!this.oneToOneOnly(word2, words3.text, remote.enabled)) return false;
|
|
14266
|
-
await chat.say(groupText.in(
|
|
14503
|
+
await chat.say(groupText.in(chat.lang()).oneToOneOnly({ word: word2 }));
|
|
14267
14504
|
return true;
|
|
14268
14505
|
}
|
|
14269
14506
|
/** A command of a 1:1 chat (`/agent attach|detach` binds the group: it stays the group's). */
|
|
@@ -14330,7 +14567,7 @@ ${quote}` : history || quote;
|
|
|
14330
14567
|
};
|
|
14331
14568
|
}
|
|
14332
14569
|
async stopReviews(chat, pulls, text8) {
|
|
14333
|
-
const zh = hasCjk2(text8);
|
|
14570
|
+
const zh = (groupChosenLang(this.ctx.deps.store, chat.scope) ?? (hasCjk2(text8) ? "zh" : "en")) === "zh";
|
|
14334
14571
|
const said2 = groupText.in(zh ? "zh" : "en");
|
|
14335
14572
|
const key = await this.ctx.credentials.credential(chat, false);
|
|
14336
14573
|
let items;
|
|
@@ -14500,7 +14737,8 @@ var Router = class _Router {
|
|
|
14500
14737
|
sessions,
|
|
14501
14738
|
this.onboarding,
|
|
14502
14739
|
new GroupRemote(this.ctx, this.text),
|
|
14503
|
-
this.starter
|
|
14740
|
+
this.starter,
|
|
14741
|
+
new GroupLanguages(this.ctx)
|
|
14504
14742
|
);
|
|
14505
14743
|
}
|
|
14506
14744
|
async handle(event) {
|
|
@@ -14635,7 +14873,7 @@ var Router = class _Router {
|
|
|
14635
14873
|
};
|
|
14636
14874
|
|
|
14637
14875
|
// packages/core/src/approvals/sensitive.ts
|
|
14638
|
-
var
|
|
14876
|
+
var WORDS2 = /* @__PURE__ */ new Set([
|
|
14639
14877
|
"token",
|
|
14640
14878
|
"secret",
|
|
14641
14879
|
"secrets",
|
|
@@ -14740,7 +14978,7 @@ function isSensitiveName(name) {
|
|
|
14740
14978
|
const parts2 = nameWords(String(name));
|
|
14741
14979
|
if (parts2.length === 0 || COUNTERS.has(parts2.join(" "))) return false;
|
|
14742
14980
|
return parts2.some((word2, i) => {
|
|
14743
|
-
if (
|
|
14981
|
+
if (WORDS2.has(word2)) return true;
|
|
14744
14982
|
const next = parts2[i + 1];
|
|
14745
14983
|
if (next === void 0) return false;
|
|
14746
14984
|
const pair = `${word2} ${next}`;
|
|
@@ -19519,7 +19757,11 @@ var terminalApprovalText = defineCatalog({
|
|
|
19519
19757
|
newText: "With:",
|
|
19520
19758
|
alwaysOption: (p) => `"Always allow (this session)" means: ${p.name}`,
|
|
19521
19759
|
firstAnswerWins: "The terminal is asking too: the first answer, here or there, counts.",
|
|
19760
|
+
afterCard: "The terminal asks only after this card is answered or its wait is over: decline it here, or hand it back to the terminal and approve it there.",
|
|
19761
|
+
onInput: "To answer at the terminal instead, type anything there: the question goes back to it and this card is withdrawn.",
|
|
19522
19762
|
tooLong: "Too long to show in full here: it can only be turned down from the phone; approve it at the terminal.",
|
|
19763
|
+
tooLongAfterCard: "Too long to show in full here: it can only be declined from the phone; the terminal asks once this card's wait is over.",
|
|
19764
|
+
tooLongOnInput: "Too long to show in full here: it can only be turned down from the phone; to approve it, type anything at the terminal and answer it there.",
|
|
19523
19765
|
questionOf: (p) => `Question ${String(p.n)} of ${String(p.of)}`,
|
|
19524
19766
|
optionYes: "Yes",
|
|
19525
19767
|
optionSession: "Always allow (this session)",
|
|
@@ -19527,6 +19769,7 @@ var terminalApprovalText = defineCatalog({
|
|
|
19527
19769
|
optionNoStop: "No, and stop",
|
|
19528
19770
|
optionPlanApprove: "Approve (ask before edits)",
|
|
19529
19771
|
optionPlanKeep: "Keep planning",
|
|
19772
|
+
optionToTerminal: "Ask me at the terminal",
|
|
19530
19773
|
answeredInTerminal: "Answered in the terminal",
|
|
19531
19774
|
noLongerWaiting: "No longer waiting for an answer",
|
|
19532
19775
|
answeredInTerminalLine: "This request was answered in the terminal.",
|
|
@@ -19550,7 +19793,11 @@ var terminalApprovalText = defineCatalog({
|
|
|
19550
19793
|
newText: "\u6539\u4E3A\uFF1A",
|
|
19551
19794
|
alwaysOption: (p) => `\u201C\u672C\u4F1A\u8BDD\u59CB\u7EC8\u5141\u8BB8\u201D\u5373\uFF1A${p.name}`,
|
|
19552
19795
|
firstAnswerWins: "\u7EC8\u7AEF\u91CC\u4E5F\u5728\u7B49\u4F60\u56DE\u7B54\uFF0C\u5148\u7B54\u7684\u7B97\u6570\u3002",
|
|
19796
|
+
afterCard: "\u7EC8\u7AEF\u8981\u7B49\u8FD9\u5F20\u5361\u7247\u7B54\u5B8C\u6216\u7B49\u5F85\u65F6\u95F4\u5230\u4E86\u624D\u4F1A\u95EE\uFF1A\u5728\u8FD9\u91CC\u62D2\u7EDD\uFF0C\u6216\u4EA4\u56DE\u7EC8\u7AEF\u3001\u5230\u7EC8\u7AEF\u91CC\u6279\u51C6\u3002",
|
|
19797
|
+
onInput: "\u60F3\u6539\u5728\u7EC8\u7AEF\u91CC\u56DE\u7B54\uFF0C\u5C31\u5728\u7EC8\u7AEF\u91CC\u968F\u4FBF\u8F93\u5165\u70B9\u4EC0\u4E48\uFF1A\u95EE\u9898\u56DE\u5230\u7EC8\u7AEF\uFF0C\u8FD9\u5F20\u5361\u7247\u968F\u4E4B\u64A4\u56DE\u3002",
|
|
19553
19798
|
tooLong: "\u5185\u5BB9\u8FC7\u957F\uFF0C\u8FD9\u91CC\u663E\u793A\u4E0D\u5168\uFF1A\u624B\u673A\u4E0A\u53EA\u80FD\u62D2\u7EDD\uFF0C\u8BF7\u5230\u7EC8\u7AEF\u6279\u51C6\u3002",
|
|
19799
|
+
tooLongAfterCard: "\u5185\u5BB9\u8FC7\u957F\uFF0C\u8FD9\u91CC\u663E\u793A\u4E0D\u5168\uFF1A\u624B\u673A\u4E0A\u53EA\u80FD\u62D2\u7EDD\uFF1B\u8FD9\u5F20\u5361\u7247\u7684\u7B49\u5F85\u65F6\u95F4\u4E00\u8FC7\uFF0C\u7EC8\u7AEF\u5C31\u4F1A\u95EE\u4F60\u3002",
|
|
19800
|
+
tooLongOnInput: "\u5185\u5BB9\u8FC7\u957F\uFF0C\u8FD9\u91CC\u663E\u793A\u4E0D\u5168\uFF1A\u624B\u673A\u4E0A\u53EA\u80FD\u62D2\u7EDD\uFF1B\u8981\u6279\u51C6\uFF0C\u5728\u7EC8\u7AEF\u91CC\u968F\u4FBF\u8F93\u5165\u70B9\u4EC0\u4E48\uFF0C\u5230\u7EC8\u7AEF\u91CC\u56DE\u7B54\u3002",
|
|
19554
19801
|
questionOf: (p) => `\u7B2C ${String(p.n)} \u9898\uFF0C\u5171 ${String(p.of)} \u9898`,
|
|
19555
19802
|
optionYes: "\u5141\u8BB8",
|
|
19556
19803
|
optionSession: "\u672C\u4F1A\u8BDD\u59CB\u7EC8\u5141\u8BB8",
|
|
@@ -19558,6 +19805,7 @@ var terminalApprovalText = defineCatalog({
|
|
|
19558
19805
|
optionNoStop: "\u62D2\u7EDD\u5E76\u505C\u6B62",
|
|
19559
19806
|
optionPlanApprove: "\u6279\u51C6\uFF08\u7F16\u8F91\u524D\u9010\u4E2A\u8BE2\u95EE\uFF09",
|
|
19560
19807
|
optionPlanKeep: "\u7EE7\u7EED\u89C4\u5212",
|
|
19808
|
+
optionToTerminal: "\u4EA4\u56DE\u7EC8\u7AEF",
|
|
19561
19809
|
answeredInTerminal: "\u5DF2\u5728\u7EC8\u7AEF\u91CC\u56DE\u7B54",
|
|
19562
19810
|
noLongerWaiting: "\u4E0D\u518D\u7B49\u5F85\u56DE\u7B54",
|
|
19563
19811
|
answeredInTerminalLine: "\u8FD9\u4E2A\u8BF7\u6C42\u5DF2\u5728\u7EC8\u7AEF\u91CC\u56DE\u7B54\u3002",
|
|
@@ -19689,6 +19937,9 @@ var TERMINAL_FALLBACK_BYTES = 4e3;
|
|
|
19689
19937
|
var LINE_MAX = 400;
|
|
19690
19938
|
var LABEL_MAX2 = 200;
|
|
19691
19939
|
var OPTION_NAME_MAX = 200;
|
|
19940
|
+
function terminalAsksOf(ask3) {
|
|
19941
|
+
return TERMINAL_ASKS.find((t4) => t4 === ask3["terminal_asks"]);
|
|
19942
|
+
}
|
|
19692
19943
|
function isTerminalAsk(ask3) {
|
|
19693
19944
|
return ask3 !== void 0 && ask3["origin"] === "terminal";
|
|
19694
19945
|
}
|
|
@@ -19711,7 +19962,8 @@ function optionTitle(option, lang) {
|
|
|
19711
19962
|
["deny", words3.optionNo],
|
|
19712
19963
|
["deny-stop", words3.optionNoStop],
|
|
19713
19964
|
["plan-approve", words3.optionPlanApprove],
|
|
19714
|
-
["plan-keep", words3.optionPlanKeep]
|
|
19965
|
+
["plan-keep", words3.optionPlanKeep],
|
|
19966
|
+
["terminal", words3.optionToTerminal]
|
|
19715
19967
|
]);
|
|
19716
19968
|
return titles.get(option.optionId);
|
|
19717
19969
|
}
|
|
@@ -19830,10 +20082,15 @@ function notesOf(ask3, complete, options, words3) {
|
|
|
19830
20082
|
const index = Number(ask3["question_index"]);
|
|
19831
20083
|
if (Number.isInteger(total) && total > 1 && Number.isInteger(index))
|
|
19832
20084
|
notes.push(words3.questionOf({ n: index + 1, of: total }));
|
|
19833
|
-
|
|
20085
|
+
const asks = terminalAsksOf(ask3);
|
|
20086
|
+
if (!complete)
|
|
20087
|
+
notes.push(
|
|
20088
|
+
asks === "after_card" ? words3.tooLongAfterCard : asks === "on_input" ? words3.tooLongOnInput : words3.tooLong
|
|
20089
|
+
);
|
|
19834
20090
|
for (const o of shownOptions(options))
|
|
19835
20091
|
if (o.kind === "allow_always") notes.push(words3.alwaysOption({ name: lit(o.name, OPTION_NAME_MAX) }));
|
|
19836
|
-
notes.push(words3.firstAnswerWins);
|
|
20092
|
+
if (asks === void 0) notes.push(words3.firstAnswerWins);
|
|
20093
|
+
else if (complete) notes.push(asks === "after_card" ? words3.afterCard : words3.onInput);
|
|
19837
20094
|
return notes;
|
|
19838
20095
|
}
|
|
19839
20096
|
function terminalAskView(input, lang = "en") {
|
|
@@ -22239,9 +22496,9 @@ var SUMMARY_CHARS = 1200;
|
|
|
22239
22496
|
var DM_SUMMARY_CHARS = 700;
|
|
22240
22497
|
var VERDICT = pyRegExp(String.raw`^\s*VERDICT:\s*(CLEAN|BLOCKING|NOT_REVIEWED)\s*$`, "i");
|
|
22241
22498
|
var CJK5 = /[一-鿿]/u;
|
|
22242
|
-
var
|
|
22499
|
+
var NAMES3 = Object.keys(PLAIN_WORDS).sort((a, b3) => b3.length - a.length).map((n) => n.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join("|");
|
|
22243
22500
|
var RAW_NAME = new RegExp(
|
|
22244
|
-
`\`(?<![A-Za-z0-9_])(${
|
|
22501
|
+
`\`(?<![A-Za-z0-9_])(${NAMES3})(?![A-Za-z0-9_])\`|(?<![A-Za-z0-9_])(${NAMES3})(?![A-Za-z0-9_])`,
|
|
22245
22502
|
"gu"
|
|
22246
22503
|
);
|
|
22247
22504
|
function plainWords(text8) {
|
|
@@ -26350,6 +26607,7 @@ __export(remote_view_exports, {
|
|
|
26350
26607
|
statusView: () => statusView,
|
|
26351
26608
|
takeoverView: () => takeoverView,
|
|
26352
26609
|
terminalAskView: () => terminalAskView,
|
|
26610
|
+
terminalAsksOf: () => terminalAsksOf,
|
|
26353
26611
|
windowView: () => windowView
|
|
26354
26612
|
});
|
|
26355
26613
|
|
|
@@ -28465,8 +28723,8 @@ var STATUS_REFUSALS = /* @__PURE__ */ new Map([
|
|
|
28465
28723
|
[403, "keyRejected"]
|
|
28466
28724
|
]);
|
|
28467
28725
|
var ERROR_CODE = /^[a-z][a-z0-9_]{0,59}$/u;
|
|
28468
|
-
function saidIn(lang,
|
|
28469
|
-
return remoteAgentText.in(lang)[
|
|
28726
|
+
function saidIn(lang, sentence3) {
|
|
28727
|
+
return remoteAgentText.in(lang)[sentence3];
|
|
28470
28728
|
}
|
|
28471
28729
|
function failureText2(err2, errors, now, lang = "en") {
|
|
28472
28730
|
if (err2 instanceof Reconnecting) return offlineText(err2.connector, now, lang);
|
|
@@ -28705,9 +28963,9 @@ var Targets = class {
|
|
|
28705
28963
|
*/
|
|
28706
28964
|
takeoverOf(chatKey, connector, agent, data, now, lang) {
|
|
28707
28965
|
const entry = resumableOf(agent).find((e) => e.ref === textOf6(data["resume"]));
|
|
28708
|
-
const no = (
|
|
28966
|
+
const no = (sentence3) => ({
|
|
28709
28967
|
entry: null,
|
|
28710
|
-
problem: saidIn(lang,
|
|
28968
|
+
problem: saidIn(lang, sentence3)
|
|
28711
28969
|
});
|
|
28712
28970
|
if (entry === void 0) return no("conversationGone");
|
|
28713
28971
|
if (entry.state === WATCH_STATE && data["takeover"]) {
|
|
@@ -28728,9 +28986,9 @@ var Targets = class {
|
|
|
28728
28986
|
* process kept when it was typed (the card carries only a token).
|
|
28729
28987
|
*/
|
|
28730
28988
|
folderOf(chatKey, agent, data, now, lang) {
|
|
28731
|
-
const no = (
|
|
28989
|
+
const no = (sentence3) => ({
|
|
28732
28990
|
path: "",
|
|
28733
|
-
problem: saidIn(lang,
|
|
28991
|
+
problem: saidIn(lang, sentence3)
|
|
28734
28992
|
});
|
|
28735
28993
|
if ("typed" in data) {
|
|
28736
28994
|
const path = this.chats.typedPath(chatKey, textOf6(data["typed"]), now);
|
|
@@ -29541,7 +29799,7 @@ function older(version, than) {
|
|
|
29541
29799
|
function isCode(reason) {
|
|
29542
29800
|
return CODE4.test(reason);
|
|
29543
29801
|
}
|
|
29544
|
-
function
|
|
29802
|
+
function sentence2(code3, lang, setup) {
|
|
29545
29803
|
const text8 = own(connectorReasonText.in(lang), code3) ?? "";
|
|
29546
29804
|
return code3 === "revoked" ? text8.replaceAll("{login}", setup.login()) : text8;
|
|
29547
29805
|
}
|
|
@@ -29565,7 +29823,7 @@ function explain(code3, version = null, setup = new Setup("", null)) {
|
|
|
29565
29823
|
if (!KNOWN.has(code3)) return both((lang) => connectorReasonHints.in(lang).unknown({ code: code3 }));
|
|
29566
29824
|
return both((lang) => {
|
|
29567
29825
|
const after = hintAfter(code3, lang, version);
|
|
29568
|
-
const own3 =
|
|
29826
|
+
const own3 = sentence2(code3, lang, setup);
|
|
29569
29827
|
return after ? `${own3}${after.sep}${after.hint}` : own3;
|
|
29570
29828
|
});
|
|
29571
29829
|
}
|
|
@@ -33198,6 +33456,7 @@ export {
|
|
|
33198
33456
|
TAG,
|
|
33199
33457
|
TARGET_LINES,
|
|
33200
33458
|
TARGET_MAX,
|
|
33459
|
+
TERMINAL_ASKS,
|
|
33201
33460
|
TERMINAL_HOOK_COMMANDS,
|
|
33202
33461
|
TERMINAL_SESSION_LIMITS,
|
|
33203
33462
|
THUMB_COMMENT_MAX,
|
|
@@ -33314,8 +33573,10 @@ export {
|
|
|
33314
33573
|
githubConfirmButtons,
|
|
33315
33574
|
githubConfirmMessage,
|
|
33316
33575
|
githubConnectMessage,
|
|
33576
|
+
groupChosenLang,
|
|
33317
33577
|
groupContextLines,
|
|
33318
33578
|
groupContextText,
|
|
33579
|
+
groupLangOf,
|
|
33319
33580
|
guardrailsText,
|
|
33320
33581
|
hasCjk2 as hasCjk,
|
|
33321
33582
|
hasControl,
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Only a choice the person said counts: a language learnt from what they write (`learnLang`), and `/lang auto`, are no
|
|
6
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
|
|
8
|
-
*
|
|
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.
|
|
9
10
|
*/
|
|
10
11
|
import type { Lang } from "../locales/lang.js";
|
|
11
12
|
/** What of a person's preferences says the choice (`Prefs.lang`, `Prefs.langSet`). */
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -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";
|
|
@@ -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,7 +139,18 @@ 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;
|
|
142
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[];
|
|
143
154
|
/** A choice a say puts to the person: from 2 to 10 options, one plain line each; `multiple` when several may be picked. */
|
|
144
155
|
export interface AssistantSayChoice {
|
|
145
156
|
options: string[];
|
|
@@ -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;
|
|
@@ -44,8 +44,9 @@ export interface TurnRun {
|
|
|
44
44
|
readonly files: readonly InboundFile[];
|
|
45
45
|
readonly lang: Lang;
|
|
46
46
|
/**
|
|
47
|
-
* The language the
|
|
48
|
-
*
|
|
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.
|
|
49
50
|
*/
|
|
50
51
|
readonly replyLang?: Lang;
|
|
51
52
|
/** The answer gets thumbs of the worker's own (a phone, whose own thumbs never reach the worker). */
|
|
@@ -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. */
|
|
@@ -97,7 +97,10 @@ 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
|
-
/**
|
|
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
|
+
*/
|
|
101
104
|
private replyLang;
|
|
102
105
|
private hooks;
|
|
103
106
|
private guarded;
|