@primusclaw/personal-agent 0.7.1 → 0.7.2

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 CHANGED
@@ -13191,7 +13191,7 @@ function parseGroupLang(text8) {
13191
13191
  var NAMES2 = {
13192
13192
  zh: ["\u4E2D\u6587", "Chinese"],
13193
13193
  en: ["\u82F1\u6587", "English"],
13194
- auto: ["\u8DDF\u968F\u63D0\u95EE\u8005\u7684\u8BED\u8A00", "the language each question is asked in"]
13194
+ auto: ["\u8DDF\u968F\u63D0\u95EE\u8005\u7684\u8BED\u8A00", "the language of each question"]
13195
13195
  };
13196
13196
  function groupLanguageSet(lang, name) {
13197
13197
  const [zh, en2] = NAMES2[lang];
@@ -13882,6 +13882,7 @@ var TurnStarter = class {
13882
13882
  }
13883
13883
  ctx;
13884
13884
  running = /* @__PURE__ */ new Set();
13885
+ askers = new AskerLanguages();
13885
13886
  /** The turns that run now (a test waits for them). */
13886
13887
  settled() {
13887
13888
  return Promise.all([...this.running]).then(() => void 0);
@@ -14052,13 +14053,18 @@ var TurnStarter = class {
14052
14053
  };
14053
14054
  }
14054
14055
  /**
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.
14056
+ * The language a chat turn is to be answered in (a review's answer follows its request's language). In a 1:1 chat the one the
14057
+ * person chose, if any. In a group the group's (`groupChosenLang`; the sender's own choice is not read there); with none set, or
14058
+ * "follow the asker", the language of the @-message's own words (`writtenLang`), else of the sender's last message in this
14059
+ * group that had some (`AskerLanguages`), else none. A group's shared session holds every member's turns, and without the line
14060
+ * the model answers in the language of that history, not the asker's.
14057
14061
  */
14058
14062
  replyLang(request) {
14059
14063
  if (request.review !== void 0) return {};
14060
14064
  const { store } = this.ctx.deps;
14061
- const lang = request.chat.scope ? groupChosenLang(store, request.chat.scope) : chosenLang(store.prefs.getPrefs(request.chat.sender.id));
14065
+ const { chat } = request;
14066
+ const asker = chat.scope ? this.askers.answer(chat.scope, chat.sender.id, request.text) : void 0;
14067
+ const lang = chat.scope ? groupChosenLang(store, chat.scope) ?? asker : chosenLang(store.prefs.getPrefs(chat.sender.id));
14062
14068
  return lang === void 0 ? {} : { replyLang: lang };
14063
14069
  }
14064
14070
  hooks(request, run, turn, turnId) {
@@ -14130,6 +14136,24 @@ var TurnStarter = class {
14130
14136
  if (created && run.repo) store.repoHistory.clearPendingRepo(userId);
14131
14137
  }
14132
14138
  };
14139
+ var ASKERS_MAX = 1e4;
14140
+ var AskerLanguages = class {
14141
+ last = /* @__PURE__ */ new Map();
14142
+ /** The language of `text` when it has words (and it is remembered), else the one remembered for `userId` in `scope`. */
14143
+ answer(scope, userId, text8) {
14144
+ const key = `${scope}
14145
+ ${userId}`;
14146
+ const own3 = writtenLang(text8);
14147
+ if (own3 === void 0) return this.last.get(key);
14148
+ this.last.delete(key);
14149
+ this.last.set(key, own3);
14150
+ if (this.last.size > ASKERS_MAX) {
14151
+ const [oldest] = this.last.keys();
14152
+ if (oldest !== void 0) this.last.delete(oldest);
14153
+ }
14154
+ return own3;
14155
+ }
14156
+ };
14133
14157
 
14134
14158
  // packages/core/src/routing/personal-text.ts
14135
14159
  var ELSEWHERE = "Files are not passed on from this chat while it talks to an agent; only your words were sent.";
@@ -33225,6 +33249,7 @@ export {
33225
33249
  ApprovalCards,
33226
33250
  ApprovalClicks,
33227
33251
  ApprovalDecisions,
33252
+ AskerLanguages,
33228
33253
  AssistantActionClicks,
33229
33254
  AssistantActions,
33230
33255
  AssistantChat as AssistantChatService,
@@ -6,7 +6,7 @@
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
7
  * message's `channel_context` (`reply_language`); to a plain session (a 1:1 chat without the assistant) as the line Claw's
8
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
+ * instead (`groupChosenLang`), or with none set the language the asker writes in: in a group a person's own choice is not read.
10
10
  */
11
11
  import type { Lang } from "../locales/lang.js";
12
12
  /** What of a person's preferences says the choice (`Prefs.lang`, `Prefs.langSet`). */
@@ -45,8 +45,8 @@ export interface TurnRun {
45
45
  readonly lang: Lang;
46
46
  /**
47
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.
48
+ * zh|en`, `chosenLang`), in a group the group's (`groupChosenLang`), or with none the asker's (`AskerLanguages`); undefined:
49
+ * none, and the answer is in the language of what they wrote. A chat turn's only: a review's follows its request.
50
50
  */
51
51
  readonly replyLang?: Lang;
52
52
  /** The answer gets thumbs of the worker's own (a phone, whose own thumbs never reach the worker). */
@@ -68,6 +68,7 @@ export interface StartTurn {
68
68
  export declare class TurnStarter {
69
69
  private readonly ctx;
70
70
  private readonly running;
71
+ private readonly askers;
71
72
  constructor(ctx: RouterContext);
72
73
  /** The turns that run now (a test waits for them). */
73
74
  settled(): Promise<void>;
@@ -98,8 +99,11 @@ export declare class TurnStarter {
98
99
  private sessionPart;
99
100
  private runOf;
100
101
  /**
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.
102
+ * The language a chat turn is to be answered in (a review's answer follows its request's language). In a 1:1 chat the one the
103
+ * person chose, if any. In a group the group's (`groupChosenLang`; the sender's own choice is not read there); with none set, or
104
+ * "follow the asker", the language of the @-message's own words (`writtenLang`), else of the sender's last message in this
105
+ * group that had some (`AskerLanguages`), else none. A group's shared session holds every member's turns, and without the line
106
+ * the model answers in the language of that history, not the asker's.
103
107
  */
104
108
  private replyLang;
105
109
  private hooks;
@@ -111,3 +115,12 @@ export declare class TurnStarter {
111
115
  */
112
116
  private recordSession;
113
117
  }
118
+ /**
119
+ * The language each member of a group last asked in there, for a message with no words of its own (`ok`, `#3`, a link): kept in
120
+ * memory, per group and member, the newest `ASKERS_MAX`. After a restart such a message gets no language until its sender writes one.
121
+ */
122
+ export declare class AskerLanguages {
123
+ private readonly last;
124
+ /** The language of `text` when it has words (and it is remembered), else the one remembered for `userId` in `scope`. */
125
+ answer(scope: string, userId: string, text: string): Lang | undefined;
126
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primusclaw/personal-agent",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "The personal agent's channel-neutral core: domain logic, ports and plugin interfaces.",
5
5
  "license": "MIT",
6
6
  "repository": {