@primusclaw/personal-agent 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +303 -36
- package/dist/types/assistant/events.d.ts +1 -0
- 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 +18 -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}`;
|
|
@@ -17337,11 +17575,17 @@ function choiceOf(event) {
|
|
|
17337
17575
|
return { options, multiple: choice["multiple"] === true };
|
|
17338
17576
|
}
|
|
17339
17577
|
var REMOTE_KINDS = /* @__PURE__ */ new Set(["done", "needs_you", "error", "resolved"]);
|
|
17578
|
+
var TERMINAL_TOOL_NAMES = {
|
|
17579
|
+
claude: "Claude Code",
|
|
17580
|
+
codex: "Codex",
|
|
17581
|
+
cursor: "Cursor"
|
|
17582
|
+
};
|
|
17340
17583
|
function resolvedOf(remote) {
|
|
17341
17584
|
const resolved = record2(remote["resolved"]);
|
|
17342
17585
|
const actionId = resolved?.["action_id"];
|
|
17343
17586
|
return resolved && typeof actionId === "string" && actionId ? { actionId, by: field2(resolved["by"]) } : void 0;
|
|
17344
17587
|
}
|
|
17588
|
+
var toolName = (tool2) => typeof tool2 === "string" && Object.hasOwn(TERMINAL_TOOL_NAMES, tool2) ? TERMINAL_TOOL_NAMES[tool2] : void 0;
|
|
17345
17589
|
function remoteNoticeOf(event) {
|
|
17346
17590
|
const remote = event["type"] === "assistant.say" ? record2(event["remote"]) : void 0;
|
|
17347
17591
|
const kind = remote?.["kind"];
|
|
@@ -17356,7 +17600,7 @@ function remoteNoticeOf(event) {
|
|
|
17356
17600
|
sessionId,
|
|
17357
17601
|
ask: record2(remote["ask"]),
|
|
17358
17602
|
machine: field2(remote["machine"]),
|
|
17359
|
-
agent: field2(remote["agent"])
|
|
17603
|
+
agent: toolName(remote["tool"]) ?? field2(remote["agent"])
|
|
17360
17604
|
};
|
|
17361
17605
|
}
|
|
17362
17606
|
|
|
@@ -19519,7 +19763,11 @@ var terminalApprovalText = defineCatalog({
|
|
|
19519
19763
|
newText: "With:",
|
|
19520
19764
|
alwaysOption: (p) => `"Always allow (this session)" means: ${p.name}`,
|
|
19521
19765
|
firstAnswerWins: "The terminal is asking too: the first answer, here or there, counts.",
|
|
19766
|
+
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.",
|
|
19767
|
+
onInput: "To answer at the terminal instead, type anything there: the question goes back to it and this card is withdrawn.",
|
|
19522
19768
|
tooLong: "Too long to show in full here: it can only be turned down from the phone; approve it at the terminal.",
|
|
19769
|
+
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.",
|
|
19770
|
+
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
19771
|
questionOf: (p) => `Question ${String(p.n)} of ${String(p.of)}`,
|
|
19524
19772
|
optionYes: "Yes",
|
|
19525
19773
|
optionSession: "Always allow (this session)",
|
|
@@ -19527,6 +19775,7 @@ var terminalApprovalText = defineCatalog({
|
|
|
19527
19775
|
optionNoStop: "No, and stop",
|
|
19528
19776
|
optionPlanApprove: "Approve (ask before edits)",
|
|
19529
19777
|
optionPlanKeep: "Keep planning",
|
|
19778
|
+
optionToTerminal: "Ask me at the terminal",
|
|
19530
19779
|
answeredInTerminal: "Answered in the terminal",
|
|
19531
19780
|
noLongerWaiting: "No longer waiting for an answer",
|
|
19532
19781
|
answeredInTerminalLine: "This request was answered in the terminal.",
|
|
@@ -19550,7 +19799,11 @@ var terminalApprovalText = defineCatalog({
|
|
|
19550
19799
|
newText: "\u6539\u4E3A\uFF1A",
|
|
19551
19800
|
alwaysOption: (p) => `\u201C\u672C\u4F1A\u8BDD\u59CB\u7EC8\u5141\u8BB8\u201D\u5373\uFF1A${p.name}`,
|
|
19552
19801
|
firstAnswerWins: "\u7EC8\u7AEF\u91CC\u4E5F\u5728\u7B49\u4F60\u56DE\u7B54\uFF0C\u5148\u7B54\u7684\u7B97\u6570\u3002",
|
|
19802
|
+
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",
|
|
19803
|
+
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
19804
|
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",
|
|
19805
|
+
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",
|
|
19806
|
+
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
19807
|
questionOf: (p) => `\u7B2C ${String(p.n)} \u9898\uFF0C\u5171 ${String(p.of)} \u9898`,
|
|
19555
19808
|
optionYes: "\u5141\u8BB8",
|
|
19556
19809
|
optionSession: "\u672C\u4F1A\u8BDD\u59CB\u7EC8\u5141\u8BB8",
|
|
@@ -19558,6 +19811,7 @@ var terminalApprovalText = defineCatalog({
|
|
|
19558
19811
|
optionNoStop: "\u62D2\u7EDD\u5E76\u505C\u6B62",
|
|
19559
19812
|
optionPlanApprove: "\u6279\u51C6\uFF08\u7F16\u8F91\u524D\u9010\u4E2A\u8BE2\u95EE\uFF09",
|
|
19560
19813
|
optionPlanKeep: "\u7EE7\u7EED\u89C4\u5212",
|
|
19814
|
+
optionToTerminal: "\u4EA4\u56DE\u7EC8\u7AEF",
|
|
19561
19815
|
answeredInTerminal: "\u5DF2\u5728\u7EC8\u7AEF\u91CC\u56DE\u7B54",
|
|
19562
19816
|
noLongerWaiting: "\u4E0D\u518D\u7B49\u5F85\u56DE\u7B54",
|
|
19563
19817
|
answeredInTerminalLine: "\u8FD9\u4E2A\u8BF7\u6C42\u5DF2\u5728\u7EC8\u7AEF\u91CC\u56DE\u7B54\u3002",
|
|
@@ -19689,6 +19943,9 @@ var TERMINAL_FALLBACK_BYTES = 4e3;
|
|
|
19689
19943
|
var LINE_MAX = 400;
|
|
19690
19944
|
var LABEL_MAX2 = 200;
|
|
19691
19945
|
var OPTION_NAME_MAX = 200;
|
|
19946
|
+
function terminalAsksOf(ask3) {
|
|
19947
|
+
return TERMINAL_ASKS.find((t4) => t4 === ask3["terminal_asks"]);
|
|
19948
|
+
}
|
|
19692
19949
|
function isTerminalAsk(ask3) {
|
|
19693
19950
|
return ask3 !== void 0 && ask3["origin"] === "terminal";
|
|
19694
19951
|
}
|
|
@@ -19711,7 +19968,8 @@ function optionTitle(option, lang) {
|
|
|
19711
19968
|
["deny", words3.optionNo],
|
|
19712
19969
|
["deny-stop", words3.optionNoStop],
|
|
19713
19970
|
["plan-approve", words3.optionPlanApprove],
|
|
19714
|
-
["plan-keep", words3.optionPlanKeep]
|
|
19971
|
+
["plan-keep", words3.optionPlanKeep],
|
|
19972
|
+
["terminal", words3.optionToTerminal]
|
|
19715
19973
|
]);
|
|
19716
19974
|
return titles.get(option.optionId);
|
|
19717
19975
|
}
|
|
@@ -19830,10 +20088,15 @@ function notesOf(ask3, complete, options, words3) {
|
|
|
19830
20088
|
const index = Number(ask3["question_index"]);
|
|
19831
20089
|
if (Number.isInteger(total) && total > 1 && Number.isInteger(index))
|
|
19832
20090
|
notes.push(words3.questionOf({ n: index + 1, of: total }));
|
|
19833
|
-
|
|
20091
|
+
const asks = terminalAsksOf(ask3);
|
|
20092
|
+
if (!complete)
|
|
20093
|
+
notes.push(
|
|
20094
|
+
asks === "after_card" ? words3.tooLongAfterCard : asks === "on_input" ? words3.tooLongOnInput : words3.tooLong
|
|
20095
|
+
);
|
|
19834
20096
|
for (const o of shownOptions(options))
|
|
19835
20097
|
if (o.kind === "allow_always") notes.push(words3.alwaysOption({ name: lit(o.name, OPTION_NAME_MAX) }));
|
|
19836
|
-
notes.push(words3.firstAnswerWins);
|
|
20098
|
+
if (asks === void 0) notes.push(words3.firstAnswerWins);
|
|
20099
|
+
else if (complete) notes.push(asks === "after_card" ? words3.afterCard : words3.onInput);
|
|
19837
20100
|
return notes;
|
|
19838
20101
|
}
|
|
19839
20102
|
function terminalAskView(input, lang = "en") {
|
|
@@ -22239,9 +22502,9 @@ var SUMMARY_CHARS = 1200;
|
|
|
22239
22502
|
var DM_SUMMARY_CHARS = 700;
|
|
22240
22503
|
var VERDICT = pyRegExp(String.raw`^\s*VERDICT:\s*(CLEAN|BLOCKING|NOT_REVIEWED)\s*$`, "i");
|
|
22241
22504
|
var CJK5 = /[一-鿿]/u;
|
|
22242
|
-
var
|
|
22505
|
+
var NAMES3 = Object.keys(PLAIN_WORDS).sort((a, b3) => b3.length - a.length).map((n) => n.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join("|");
|
|
22243
22506
|
var RAW_NAME = new RegExp(
|
|
22244
|
-
`\`(?<![A-Za-z0-9_])(${
|
|
22507
|
+
`\`(?<![A-Za-z0-9_])(${NAMES3})(?![A-Za-z0-9_])\`|(?<![A-Za-z0-9_])(${NAMES3})(?![A-Za-z0-9_])`,
|
|
22245
22508
|
"gu"
|
|
22246
22509
|
);
|
|
22247
22510
|
function plainWords(text8) {
|
|
@@ -26350,6 +26613,7 @@ __export(remote_view_exports, {
|
|
|
26350
26613
|
statusView: () => statusView,
|
|
26351
26614
|
takeoverView: () => takeoverView,
|
|
26352
26615
|
terminalAskView: () => terminalAskView,
|
|
26616
|
+
terminalAsksOf: () => terminalAsksOf,
|
|
26353
26617
|
windowView: () => windowView
|
|
26354
26618
|
});
|
|
26355
26619
|
|
|
@@ -28465,8 +28729,8 @@ var STATUS_REFUSALS = /* @__PURE__ */ new Map([
|
|
|
28465
28729
|
[403, "keyRejected"]
|
|
28466
28730
|
]);
|
|
28467
28731
|
var ERROR_CODE = /^[a-z][a-z0-9_]{0,59}$/u;
|
|
28468
|
-
function saidIn(lang,
|
|
28469
|
-
return remoteAgentText.in(lang)[
|
|
28732
|
+
function saidIn(lang, sentence3) {
|
|
28733
|
+
return remoteAgentText.in(lang)[sentence3];
|
|
28470
28734
|
}
|
|
28471
28735
|
function failureText2(err2, errors, now, lang = "en") {
|
|
28472
28736
|
if (err2 instanceof Reconnecting) return offlineText(err2.connector, now, lang);
|
|
@@ -28705,9 +28969,9 @@ var Targets = class {
|
|
|
28705
28969
|
*/
|
|
28706
28970
|
takeoverOf(chatKey, connector, agent, data, now, lang) {
|
|
28707
28971
|
const entry = resumableOf(agent).find((e) => e.ref === textOf6(data["resume"]));
|
|
28708
|
-
const no = (
|
|
28972
|
+
const no = (sentence3) => ({
|
|
28709
28973
|
entry: null,
|
|
28710
|
-
problem: saidIn(lang,
|
|
28974
|
+
problem: saidIn(lang, sentence3)
|
|
28711
28975
|
});
|
|
28712
28976
|
if (entry === void 0) return no("conversationGone");
|
|
28713
28977
|
if (entry.state === WATCH_STATE && data["takeover"]) {
|
|
@@ -28728,9 +28992,9 @@ var Targets = class {
|
|
|
28728
28992
|
* process kept when it was typed (the card carries only a token).
|
|
28729
28993
|
*/
|
|
28730
28994
|
folderOf(chatKey, agent, data, now, lang) {
|
|
28731
|
-
const no = (
|
|
28995
|
+
const no = (sentence3) => ({
|
|
28732
28996
|
path: "",
|
|
28733
|
-
problem: saidIn(lang,
|
|
28997
|
+
problem: saidIn(lang, sentence3)
|
|
28734
28998
|
});
|
|
28735
28999
|
if ("typed" in data) {
|
|
28736
29000
|
const path = this.chats.typedPath(chatKey, textOf6(data["typed"]), now);
|
|
@@ -29541,7 +29805,7 @@ function older(version, than) {
|
|
|
29541
29805
|
function isCode(reason) {
|
|
29542
29806
|
return CODE4.test(reason);
|
|
29543
29807
|
}
|
|
29544
|
-
function
|
|
29808
|
+
function sentence2(code3, lang, setup) {
|
|
29545
29809
|
const text8 = own(connectorReasonText.in(lang), code3) ?? "";
|
|
29546
29810
|
return code3 === "revoked" ? text8.replaceAll("{login}", setup.login()) : text8;
|
|
29547
29811
|
}
|
|
@@ -29565,7 +29829,7 @@ function explain(code3, version = null, setup = new Setup("", null)) {
|
|
|
29565
29829
|
if (!KNOWN.has(code3)) return both((lang) => connectorReasonHints.in(lang).unknown({ code: code3 }));
|
|
29566
29830
|
return both((lang) => {
|
|
29567
29831
|
const after = hintAfter(code3, lang, version);
|
|
29568
|
-
const own3 =
|
|
29832
|
+
const own3 = sentence2(code3, lang, setup);
|
|
29569
29833
|
return after ? `${own3}${after.sep}${after.hint}` : own3;
|
|
29570
29834
|
});
|
|
29571
29835
|
}
|
|
@@ -33198,6 +33462,7 @@ export {
|
|
|
33198
33462
|
TAG,
|
|
33199
33463
|
TARGET_LINES,
|
|
33200
33464
|
TARGET_MAX,
|
|
33465
|
+
TERMINAL_ASKS,
|
|
33201
33466
|
TERMINAL_HOOK_COMMANDS,
|
|
33202
33467
|
TERMINAL_SESSION_LIMITS,
|
|
33203
33468
|
THUMB_COMMENT_MAX,
|
|
@@ -33314,8 +33579,10 @@ export {
|
|
|
33314
33579
|
githubConfirmButtons,
|
|
33315
33580
|
githubConfirmMessage,
|
|
33316
33581
|
githubConnectMessage,
|
|
33582
|
+
groupChosenLang,
|
|
33317
33583
|
groupContextLines,
|
|
33318
33584
|
groupContextText,
|
|
33585
|
+
groupLangOf,
|
|
33319
33586
|
guardrailsText,
|
|
33320
33587
|
hasCjk2 as hasCjk,
|
|
33321
33588
|
hasControl,
|
|
@@ -64,6 +64,7 @@ export interface RemoteNotice {
|
|
|
64
64
|
readonly sessionId: string;
|
|
65
65
|
readonly ask: Event | undefined;
|
|
66
66
|
readonly machine: string;
|
|
67
|
+
/** Who asked, as the person knows it: the tool's name (Claude Code, Codex, Cursor) when Claw said which, else the agent's id. */
|
|
67
68
|
readonly agent: string;
|
|
68
69
|
readonly resolved?: {
|
|
69
70
|
readonly actionId: string;
|
|
@@ -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";
|
|
@@ -101,6 +101,11 @@ export interface AssistantRemoteNotice {
|
|
|
101
101
|
session_id: string;
|
|
102
102
|
machine: string;
|
|
103
103
|
agent: string;
|
|
104
|
+
/**
|
|
105
|
+
* A terminal's request (`ask.origin` "terminal"): which tool asked there (`claude`, `codex`, `cursor`), for a channel to name it as its
|
|
106
|
+
* users know it (Claude Code, Codex, Cursor) instead of by `agent`, the connector's id for it. Absent when the connector did not say.
|
|
107
|
+
*/
|
|
108
|
+
tool?: "claude" | "codex" | "cursor";
|
|
104
109
|
ask?: AssistantRemoteAsk;
|
|
105
110
|
/** With `kind: "resolved"`: which request, and who ended it (`terminal`, or `timeout`, `session_end`, `connector_restart` when nobody did). */
|
|
106
111
|
resolved?: {
|
|
@@ -121,8 +126,8 @@ export interface AssistantRemoteAsk {
|
|
|
121
126
|
/** When the connector stops waiting for an answer (it refuses the request then); absent when it did not say. */
|
|
122
127
|
expires_at?: string;
|
|
123
128
|
/**
|
|
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.
|
|
129
|
+
* `terminal`: asked in a terminal on the machine (a conversation its person started there), which asks it too (when: see
|
|
130
|
+
* `terminal_asks`); whichever answer comes first is taken. Absent for a session opened through Claw.
|
|
126
131
|
*/
|
|
127
132
|
origin?: "terminal";
|
|
128
133
|
/** A terminal's request: what kind of prompt it is (`execute`, `edit`, `question`, `switch_mode` for a plan, `mcp`, `other`). */
|
|
@@ -139,7 +144,18 @@ export interface AssistantRemoteAsk {
|
|
|
139
144
|
/** A terminal's question asked as several cards: which this is (from 0), and how many. */
|
|
140
145
|
question_index?: number;
|
|
141
146
|
question_count?: number;
|
|
147
|
+
/**
|
|
148
|
+
* A terminal's request: when the terminal asks it too, as its connector said (`terminal_asks` on the request). Absent: alongside
|
|
149
|
+
* the card, and the first answer counts (Claude Code). `after_card`: only once the card is answered or its time is up, and the
|
|
150
|
+
* card offers only to decline or to hand it back to the terminal (Cursor). `on_input`: only once someone types in that terminal,
|
|
151
|
+
* which withdraws the card (Codex).
|
|
152
|
+
*/
|
|
153
|
+
terminal_asks?: TerminalAsks;
|
|
142
154
|
}
|
|
155
|
+
/** When a terminal asks a request that is also on a card, besides alongside it: see {@link AssistantRemoteAsk.terminal_asks}. */
|
|
156
|
+
export type TerminalAsks = "after_card" | "on_input";
|
|
157
|
+
/** The values of `terminal_asks` a channel knows; any other is read as absent. */
|
|
158
|
+
export declare const TERMINAL_ASKS: readonly TerminalAsks[];
|
|
143
159
|
/** A choice a say puts to the person: from 2 to 10 options, one plain line each; `multiple` when several may be picked. */
|
|
144
160
|
export interface AssistantSayChoice {
|
|
145
161
|
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;
|