@istarry/dsh-assistant-profile 0.4.5 → 0.5.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/lib/index.js CHANGED
@@ -1,11 +1,8 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
- import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
2
+ import { Service } from "@deepseek-ai/cordis";
3
3
  import { defineTool } from "@deepseek-ai/dsh-tools";
4
- import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
5
- import { SessionId } from "@deepseek-ai/dsh-session";
6
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
- import { dirname, join } from "node:path";
8
- import { dshHomePath } from "@deepseek-ai/dsh-home-paths";
4
+ import "@deepseek-ai/dsh-llm";
5
+ import "@deepseek-ai/dsh-session";
9
6
  //#region ../tooling/node_modules/.pnpm/js-yaml@4.2.0/node_modules/js-yaml/dist/js-yaml.mjs
10
7
  /*! js-yaml 4.2.0 https://github.com/nodeca/js-yaml @license MIT */
11
8
  var __create = Object.create;
@@ -2416,45 +2413,35 @@ const DEFAULT_PERSONA = {
2416
2413
  //#endregion
2417
2414
  //#region ../.build/tsc/memory.js
2418
2415
  /**
2419
- * 助理记忆的 Markdown 存储。
2416
+ * 助理记忆的存储形态:**一段 markdown 文本**,存在本插件的配置字段里
2417
+ * (`memoryText`),而不是独立的文件。
2420
2418
  *
2421
- * 只有一个档案,所以只有一份记忆:`<dshHome>/assistant-profile/memory.md`。
2422
- * 路径经 `dshHomePath` 解析,`$DSH_HOME` 覆盖与跨平台 home 都能落对位置。
2419
+ * 为什么不用文件:记忆要能被设置页读写、能随 profile 持久化、能在注入时同步取到。
2420
+ * 官方设置通道(`configForms` → profile 的 Cordis patch)对**标量字段**支持最好,
2421
+ * 而自定义 RPC 需要 typert 生成物,那条路在本插件里没走通(client 半端加载器会跳过
2422
+ * 带生成 descriptors 的 bundle)。所以把整段文本当作一个配置项:编辑器保存 = 写配置,
2423
+ * 注入 = 读配置,重启后仍在,全程不经过远程调用。
2423
2424
  *
2424
- * 文档形状:`# 标题` 之类的自由文本由用户自己写,每条记忆是
2425
- * `- [YYYY-MM-DD HH:mm] 内容` 一行。时间戳是条目的主元数据——重存同一件事会刷新
2426
- * 时间戳而不是复制一行。
2427
- *
2428
- * 读写都是同步的:提示词组装调用 section thunk 时没有 await 点,而文件很小。
2425
+ * 文本形状与 agent-suite 的 memory.md 保持一致,便于以后迁移:
2426
+ * `# 标题` 之类的自由文本由用户写,每条记忆是一行 `- [YYYY-MM-DD HH:mm] 内容`。
2429
2427
  *
2430
2428
  * @module @istarry/dsh-assistant-profile/memory
2431
2429
  */
2432
- /** 本包在 harness home 下占用的目录名。 */
2433
- const MEMORY_DIR = "assistant-profile";
2434
- /** 记忆文档的文件名。 */
2435
- const MEMORY_FILE = "memory.md";
2436
2430
  const ENTRY_RE = /^-?\s*\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2})\]\s*(.*)$/;
2437
- /** 把本地时间格式化成条目的时间戳。 */
2431
+ /**
2432
+ * 把本地时间格式化成条目的时间戳。
2433
+ * @param date - 时间来源,测试可注入。
2434
+ * @returns `YYYY-MM-DD HH:mm`。
2435
+ */
2438
2436
  function stampNow(date = /* @__PURE__ */ new Date()) {
2439
2437
  const pad = (n) => String(n).padStart(2, "0");
2440
2438
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
2441
2439
  }
2442
- /** 记忆文档的绝对路径。 */
2443
- function memoryPath() {
2444
- return join(dshHomePath(MEMORY_DIR), MEMORY_FILE);
2445
- }
2446
- /** 读取整篇记忆文档;文件不存在时返回空串。 */
2447
- function readMemoryText() {
2448
- const path = memoryPath();
2449
- return existsSync(path) ? readFileSync(path, "utf8") : "";
2450
- }
2451
- /** 整体替换记忆文档(用户编辑框里的保存)。 */
2452
- function writeMemory(text) {
2453
- const path = memoryPath();
2454
- mkdirSync(dirname(path), { recursive: true });
2455
- writeFileSync(path, text, "utf8");
2456
- }
2457
- /** 从文档正文里解析出所有记忆条目。 */
2440
+ /**
2441
+ * 从文本里解析出所有记忆条目。
2442
+ * @param text - 整段记忆文本。
2443
+ * @returns 按出现顺序的条目。
2444
+ */
2458
2445
  function parseEntries(text) {
2459
2446
  const entries = [];
2460
2447
  for (const line of text.split(/\r?\n/)) {
@@ -2468,30 +2455,29 @@ function parseEntries(text) {
2468
2455
  return entries;
2469
2456
  }
2470
2457
  /**
2471
- * 去重比较键:内容大小写折叠、连续空白压成一个空格。
2472
- * 「记得 DSH 路径」和「记得DSH路径」因此算同一条。
2473
- * @param content - 原始条目内容。
2474
- * @returns 用于比较的规范键。
2458
+ * 去重比较键:大小写折叠、连续空白压成一个。
2459
+ * @param content - 条目内容。
2460
+ * @returns 规范键。
2475
2461
  */
2476
2462
  function dedupeKey(content) {
2477
2463
  return content.trim().toLowerCase().replace(/\s+/g, " ");
2478
2464
  }
2479
2465
  /**
2480
- * 追加一条记忆,对整篇文档去重:已存在同义条目就刷新它的时间戳,否则追加新行。
2481
- * 非条目行(标题、空行)原样保留。
2482
- * @param content - 要记住的事实(单行文本)。
2483
- * @param at - 时间戳来源,测试可注入。
2484
- * @returns 这次写入做了什么。
2466
+ * 往整段记忆文本里追加一条事实,并对已有条目去重:
2467
+ * 同义条目刷新时间戳,否则追加新行;非条目行(标题、空行)原样保留。
2468
+ * @param text - 现有整段文本。
2469
+ * @param content - 要记住的事实。
2470
+ * @param at - 时间来源。
2471
+ * @returns 追加结果,含新文本。
2485
2472
  */
2486
- function appendMemory(content, at = /* @__PURE__ */ new Date()) {
2487
- const text = readMemoryText();
2488
- const stamp = stampNow(at);
2473
+ function appendToText(text, content, at = /* @__PURE__ */ new Date()) {
2489
2474
  const trimmed = content.trim();
2490
2475
  if (trimmed === "") return {
2491
2476
  appended: false,
2492
2477
  refreshed: false,
2493
- content: trimmed
2478
+ text
2494
2479
  };
2480
+ const stamp = stampNow(at);
2495
2481
  const key = dedupeKey(trimmed);
2496
2482
  const headerLines = [];
2497
2483
  const entryLines = [];
@@ -2502,29 +2488,27 @@ function appendMemory(content, at = /* @__PURE__ */ new Date()) {
2502
2488
  if (match === null) continue;
2503
2489
  if (dedupeKey((match[2] ?? "").trim()) === key) {
2504
2490
  entryLines[index] = `- [${stamp}] ${trimmed}`;
2505
- writeChunked(headerLines, entryLines);
2506
2491
  return {
2507
2492
  appended: false,
2508
2493
  refreshed: true,
2509
- content: trimmed
2494
+ text: compose(headerLines, entryLines)
2510
2495
  };
2511
2496
  }
2512
2497
  }
2513
2498
  entryLines.push(`- [${stamp}] ${trimmed}`);
2514
- writeChunked(headerLines, entryLines);
2515
2499
  return {
2516
2500
  appended: true,
2517
2501
  refreshed: false,
2518
- content: trimmed
2502
+ text: compose(headerLines, entryLines)
2519
2503
  };
2520
2504
  }
2521
- /** 把标题行与条目行拼回一篇文档并写入(顺手收掉首尾空行)。 */
2522
- function writeChunked(headerLines, entryLines) {
2505
+ /** 拼回整段文本,顺手收掉标题块首尾的空行(避免反复追加时越积越多)。 */
2506
+ function compose(headerLines, entryLines) {
2523
2507
  const meaningful = [...headerLines];
2524
2508
  while (meaningful.length > 0 && /^\s*$/.test(meaningful[0])) meaningful.shift();
2525
2509
  while (meaningful.length > 0 && /^\s*$/.test(meaningful[meaningful.length - 1])) meaningful.pop();
2526
2510
  const body = [...meaningful, ...entryLines].join("\n");
2527
- writeMemory(body.endsWith("\n") ? body : `${body}\n`);
2511
+ return body.endsWith("\n") ? body : `${body}\n`;
2528
2512
  }
2529
2513
  //#endregion
2530
2514
  //#region ../.build/tsc/index.js
@@ -2556,44 +2540,6 @@ function writeChunked(headerLines, entryLines) {
2556
2540
  *
2557
2541
  * @module @istarry/dsh-assistant-profile
2558
2542
  */
2559
- var __runInitializers = function(thisArg, initializers, value) {
2560
- var useValue = arguments.length > 2;
2561
- for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
2562
- return useValue ? value : void 0;
2563
- };
2564
- var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2565
- function accept(f) {
2566
- if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
2567
- return f;
2568
- }
2569
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
2570
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
2571
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
2572
- var _, done = false;
2573
- for (var i = decorators.length - 1; i >= 0; i--) {
2574
- var context = {};
2575
- for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
2576
- for (var p in contextIn.access) context.access[p] = contextIn.access[p];
2577
- context.addInitializer = function(f) {
2578
- if (done) throw new TypeError("Cannot add initializers after decoration has completed");
2579
- extraInitializers.push(accept(f || null));
2580
- };
2581
- var result = (0, decorators[i])(kind === "accessor" ? {
2582
- get: descriptor.get,
2583
- set: descriptor.set
2584
- } : descriptor[key], context);
2585
- if (kind === "accessor") {
2586
- if (result === void 0) continue;
2587
- if (result === null || typeof result !== "object") throw new TypeError("Object expected");
2588
- if (_ = accept(result.get)) descriptor.get = _;
2589
- if (_ = accept(result.set)) descriptor.set = _;
2590
- if (_ = accept(result.init)) initializers.unshift(_);
2591
- } else if (_ = accept(result)) if (kind === "field") initializers.unshift(_);
2592
- else descriptor[key] = _;
2593
- }
2594
- if (target) Object.defineProperty(target, contextIn.name, descriptor);
2595
- done = true;
2596
- };
2597
2543
  /** Cordis 插件名。 */
2598
2544
  const name = "assistant-profile";
2599
2545
  /** 人格所在的那一段的规范名——预设与档案共用同一个名字,遮蔽才成立。 */
@@ -2616,7 +2562,8 @@ const Config = z.object({
2616
2562
  persona: z.string().default(DEFAULT_PERSONA.persona).volatile(),
2617
2563
  customsJson: z.string().default("[]").volatile(),
2618
2564
  memoryEnabled: z.boolean().default(true).description("Inject the assistant memory into the system prompt").volatile(),
2619
- autoMemory: z.boolean().default(false).description("Let the model write memories on its own judgment (requires memoryEnabled)").volatile()
2565
+ autoMemory: z.boolean().default(false).description("Let the model write memories on its own judgment (requires memoryEnabled)").volatile(),
2566
+ memoryText: z.string().default("").description("The assistant memory document").volatile()
2620
2567
  });
2621
2568
  /** 跨插件重载共享的当前配置引用:旧闭包也能读到新值。 */
2622
2569
  const LIVE = Symbol.for("dsh.assistant-profile.config");
@@ -2737,273 +2684,111 @@ const MEMORY_ADD_VALUE_SCHEMA = {
2737
2684
  content: { type: "string" }
2738
2685
  }
2739
2686
  };
2740
- /** 把选中的对话文本提炼成记忆条目的指令(与 agent-suite 一致的规则)。 */
2741
- const REMEMBER_INSTRUCTION = "你是一个长期记忆提炼引擎。用户从对话中圈选了下面这段内容,明确希望把它记入助手的长期记忆。请把这些内容提炼成简短、稳定、可跨会话复用的记忆条目。\n规则:\n- 每行输出一条记忆内容本身(不带时间戳、不带 \"- \" 前缀、不带编号)。\n- 用户既然圈选了它,请默认提取出其中明确、稳定的信息:称呼、偏好、习惯、约定、身份事实、项目背景。\n- 把相关事实合并成一条;不要逐字复述,只提炼结论,但保留关键名称、数字、关系。\n- 只有当整段内容完全是临时任务细节或含凭据、密钥时,才不输出条目(此时只输出一个真正的空行)。\n- 用与对话片段一致的语言书写(对话是中文就用中文,是英文就用英文)。\n- 严禁输出\"空行\"\"(空行)\"\"无\"\"没有\"\"none\"之类的占位文字。";
2742
- let AssistantProfile = (() => {
2743
- let _classSuper = TypertRemoteService;
2744
- let _instanceExtraInitializers = [];
2745
- let _readGlobal_decorators;
2746
- let _writeGlobal_decorators;
2747
- let _remember_decorators;
2748
- return class AssistantProfile extends _classSuper {
2749
- static {
2750
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
2751
- _readGlobal_decorators = [Remote("readGlobal")];
2752
- _writeGlobal_decorators = [Remote("writeGlobal")];
2753
- _remember_decorators = [Remote("remember")];
2754
- __esDecorate(this, null, _readGlobal_decorators, {
2755
- kind: "method",
2756
- name: "readGlobal",
2757
- static: false,
2758
- private: false,
2759
- access: {
2760
- has: (obj) => "readGlobal" in obj,
2761
- get: (obj) => obj.readGlobal
2762
- },
2763
- metadata: _metadata
2764
- }, null, _instanceExtraInitializers);
2765
- __esDecorate(this, null, _writeGlobal_decorators, {
2766
- kind: "method",
2767
- name: "writeGlobal",
2768
- static: false,
2769
- private: false,
2770
- access: {
2771
- has: (obj) => "writeGlobal" in obj,
2772
- get: (obj) => obj.writeGlobal
2773
- },
2774
- metadata: _metadata
2775
- }, null, _instanceExtraInitializers);
2776
- __esDecorate(this, null, _remember_decorators, {
2777
- kind: "method",
2778
- name: "remember",
2779
- static: false,
2780
- private: false,
2781
- access: {
2782
- has: (obj) => "remember" in obj,
2783
- get: (obj) => obj.remember
2784
- },
2785
- metadata: _metadata
2786
- }, null, _instanceExtraInitializers);
2787
- if (_metadata) Object.defineProperty(this, Symbol.metadata, {
2788
- enumerable: true,
2789
- configurable: true,
2790
- writable: true,
2791
- value: _metadata
2792
- });
2793
- }
2794
- config = __runInitializers(this, _instanceExtraInitializers);
2795
- static inject = [
2796
- "systemPrompt",
2797
- "tools",
2798
- "llm"
2799
- ];
2800
- static Config = Config;
2801
- /** `memory_add` 工具的注册 disposer,只在开关允许时存在。 */
2802
- toolDisposer;
2803
- constructor(ctx, config) {
2804
- super(ctx, "assistantProfile");
2805
- this.config = config;
2806
- live.config = config;
2807
- ctx.inject(["settings"], (child) => {
2808
- child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
2809
- });
2810
- ctx.effect(() => ctx.systemPrompt.section({
2811
- name: MEMORY_SECTION,
2812
- order: MEMORY_ORDER,
2813
- text: () => {
2814
- if (config.memoryEnabled.get() !== true) return "";
2815
- return readMemoryText().trim();
2816
- }
2817
- }), "assistant-profile: memory section");
2818
- const reinstall = (agent) => installPersona(ctx, agent).catch((error) => {
2819
- ctx.logger.warn(`assistant-profile: persona install failed: ${String(error)}`);
2820
- });
2821
- ctx.effect(() => ctx.on("agent/created", ({ agent }) => {
2822
- reinstall(agent);
2823
- }), "assistant-profile: persona");
2824
- ctx.effect(() => ctx.on("agent-preset/selected", (sessionId) => {
2825
- const agent = ctx.get("agents")?.get(String(sessionId));
2826
- if (agent !== void 0) reinstall(agent);
2827
- }), "assistant-profile: preset switch");
2828
- this.syncTool();
2829
- ctx.on("loader/volatile-update", () => {
2830
- this.syncTool();
2831
- });
2832
- }
2833
- /** 按当前开关注册或撤下 `memory_add` 工具。 */
2834
- syncTool() {
2835
- const want = this.config.memoryEnabled.get() === true && this.config.autoMemory.get() === true;
2836
- if (want && this.toolDisposer === void 0) this.toolDisposer = this.registerTool();
2837
- else if (!want && this.toolDisposer !== void 0) {
2838
- this.toolDisposer();
2839
- this.toolDisposer = void 0;
2687
+ /**
2688
+ * 助理档案服务:人格注入 + 单份长期记忆。
2689
+ *
2690
+ * 记忆的存储、注入、两个开关、`memory_add` 工具和按钮提炼都在这里;
2691
+ * 客户端只负责设置页与弹窗。
2692
+ */
2693
+ var AssistantProfile = class extends Service {
2694
+ config;
2695
+ static inject = [
2696
+ "systemPrompt",
2697
+ "tools",
2698
+ "llm"
2699
+ ];
2700
+ static Config = Config;
2701
+ /** `memory_add` 工具的注册 disposer,只在开关允许时存在。 */
2702
+ toolDisposer;
2703
+ constructor(ctx, config) {
2704
+ super(ctx);
2705
+ this.config = config;
2706
+ live.config = config;
2707
+ ctx.inject(["settings"], (child) => {
2708
+ child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
2709
+ });
2710
+ ctx.effect(() => ctx.systemPrompt.section({
2711
+ name: MEMORY_SECTION,
2712
+ order: MEMORY_ORDER,
2713
+ text: () => {
2714
+ if (config.memoryEnabled.get() !== true) return "";
2715
+ return config.memoryText.get().trim();
2840
2716
  }
2841
- }
2842
- /** 注册模型侧的记忆工具,返回撤下函数。 */
2843
- registerTool() {
2844
- const ctx = this.ctx;
2845
- const disposer = ctx.tools.register(defineTool({
2846
- name: "memory_add",
2847
- description: MEMORY_ADD_DESCRIPTION,
2848
- parameters: { text: {
2849
- type: "string",
2850
- required: true,
2851
- description: "The stable fact to remember, in its final prose form."
2852
- } },
2853
- output: {
2854
- schema: MEMORY_ADD_VALUE_SCHEMA,
2855
- render: (_args, value) => {
2856
- const v = value;
2857
- if (v.saved) return [{
2858
- type: "text",
2859
- text: `记住:${v.content ?? ""}`
2860
- }];
2861
- return [{
2862
- type: "text",
2863
- text: `未记住:${v.outcome ?? "未启用"}`
2864
- }];
2865
- }
2866
- },
2867
- execute: async (args, _exec) => {
2868
- const trimmed = args.text.trim();
2869
- if (trimmed === "") return {
2870
- saved: false,
2871
- outcome: "empty fact"
2872
- };
2873
- const result = appendMemory(trimmed);
2874
- return {
2875
- saved: true,
2876
- content: result.content,
2877
- outcome: result.appended ? "appended" : result.refreshed ? "refreshed" : "unchanged"
2878
- };
2717
+ }), "assistant-profile: memory section");
2718
+ const reinstall = (agent) => installPersona(ctx, agent).catch((error) => {
2719
+ ctx.logger.warn(`assistant-profile: persona install failed: ${String(error)}`);
2720
+ });
2721
+ ctx.effect(() => ctx.on("agent/created", ({ agent }) => {
2722
+ reinstall(agent);
2723
+ }), "assistant-profile: persona");
2724
+ ctx.effect(() => ctx.on("agent-preset/selected", (sessionId) => {
2725
+ const agent = ctx.get("agents")?.get(String(sessionId));
2726
+ if (agent !== void 0) reinstall(agent);
2727
+ }), "assistant-profile: preset switch");
2728
+ this.syncTool();
2729
+ ctx.on("loader/volatile-update", () => {
2730
+ this.syncTool();
2731
+ });
2732
+ }
2733
+ /** 按当前开关注册或撤下 `memory_add` 工具。 */
2734
+ syncTool() {
2735
+ const want = this.config.memoryEnabled.get() === true && this.config.autoMemory.get() === true;
2736
+ if (want && this.toolDisposer === void 0) this.toolDisposer = this.registerTool();
2737
+ else if (!want && this.toolDisposer !== void 0) {
2738
+ this.toolDisposer();
2739
+ this.toolDisposer = void 0;
2740
+ }
2741
+ }
2742
+ /** 注册模型侧的记忆工具,返回撤下函数。 */
2743
+ registerTool() {
2744
+ const ctx = this.ctx;
2745
+ const disposer = ctx.tools.register(defineTool({
2746
+ name: "memory_add",
2747
+ description: MEMORY_ADD_DESCRIPTION,
2748
+ parameters: { text: {
2749
+ type: "string",
2750
+ required: true,
2751
+ description: "The stable fact to remember, in its final prose form."
2752
+ } },
2753
+ output: {
2754
+ schema: MEMORY_ADD_VALUE_SCHEMA,
2755
+ render: (_args, value) => {
2756
+ const v = value;
2757
+ if (v.saved) return [{
2758
+ type: "text",
2759
+ text: `记住:${v.content ?? ""}`
2760
+ }];
2761
+ return [{
2762
+ type: "text",
2763
+ text: `未记住:${v.outcome ?? "未启用"}`
2764
+ }];
2879
2765
  }
2880
- }));
2881
- const teardown = () => {
2882
- disposer();
2883
- this.toolDisposer = void 0;
2884
- };
2885
- ctx.effect(() => teardown, "assistant-profile: memory tool");
2886
- return () => {
2887
- disposer();
2888
- };
2889
- }
2890
- /** 读取记忆全文(设置页编辑框)。 */
2891
- readGlobal() {
2892
- return readMemoryText();
2893
- }
2894
- /** 整体保存记忆(设置页编辑框的保存按钮)。 */
2895
- writeGlobal(text) {
2896
- writeMemory(text);
2897
- }
2898
- /** 追加一条记忆(内部使用/将来的其它入口)。 */
2899
- appendMemory(content) {
2900
- return appendMemory(content);
2901
- }
2902
- /**
2903
- * 用某个会话自己的模型,把选中的对话文本提炼成记忆条目后追加。
2904
- *
2905
- * 这是 UI 按钮的路径:用户在弹窗里选一段聊天记录,模型把其中稳定的事实抽出来。
2906
- * 会话必须是活的(它的 agent 提供模型配置),否则没有可用的 provider/model。
2907
- * @param sessionId - 执行提炼的会话。
2908
- * @param text - 选中的对话文本。
2909
- * @returns 是否写入、写入了哪些条目,以及失败原因。
2910
- */
2911
- async remember(sessionId, text) {
2912
- const agent = this.ctx.get("agents")?.get(SessionId(sessionId));
2913
- if (agent === void 0) return {
2914
- saved: false,
2915
- outcome: "session not open"
2916
- };
2917
- const callConfig = (agent.session.requestHeader?.())?.config;
2918
- if (callConfig === void 0) return {
2919
- saved: false,
2920
- outcome: "no provider/model available"
2921
- };
2922
- const trimmed = text.trim();
2923
- if (trimmed === "") return {
2924
- saved: false,
2925
- outcome: "empty selection"
2926
- };
2927
- const assembler = new BlockAssembler();
2928
- const messages = [createUserMessage({
2929
- content: [{
2930
- type: "text",
2931
- text: trimmed
2932
- }],
2933
- source: { kind: "user" }
2934
- }), createUserMessage({
2935
- content: [{
2936
- type: "text",
2937
- text: REMEMBER_INSTRUCTION
2938
- }],
2939
- source: { kind: "user" }
2940
- })];
2941
- try {
2942
- for await (const chunk of this.ctx.llm.stream({
2943
- ...callConfig,
2944
- messages,
2945
- sessionId: SessionId(sessionId)
2946
- })) assembler.push(chunk);
2947
- } catch (error) {
2948
- return {
2766
+ },
2767
+ execute: async (args, _exec) => {
2768
+ const trimmed = args.text.trim();
2769
+ if (trimmed === "") return {
2949
2770
  saved: false,
2950
- outcome: `analysis failed: ${errorChain(error)}`
2771
+ outcome: "empty fact"
2772
+ };
2773
+ const result = appendToText(this.config.memoryText.get(), trimmed);
2774
+ this.config.memoryText.set(result.text);
2775
+ return {
2776
+ saved: true,
2777
+ content: result.content,
2778
+ outcome: result.appended ? "appended" : result.refreshed ? "refreshed" : "unchanged"
2951
2779
  };
2952
2780
  }
2953
- const failure = rememberFinishError(assembler.finish);
2954
- if (failure !== void 0) return {
2955
- saved: false,
2956
- outcome: failure
2957
- };
2958
- const entries = splitEntries(assembler.blocks());
2959
- if (entries.length === 0) return {
2960
- saved: false,
2961
- outcome: "nothing worth remembering"
2962
- };
2963
- for (const entry of entries) appendMemory(entry);
2964
- return {
2965
- saved: true,
2966
- entries,
2967
- outcome: `remembered ${entries.length} entries`
2968
- };
2969
- }
2970
- };
2971
- })();
2972
- /** 把错误及其 cause 链压成一行。 */
2973
- function errorChain(error) {
2974
- const parts = [];
2975
- let current = error;
2976
- while (typeof current === "object" && current !== null && parts.length < 4) {
2977
- const message = current.message;
2978
- if (typeof message !== "string" || message === "") break;
2979
- parts.push(message);
2980
- current = current.cause;
2981
- }
2982
- return parts.join(" | ");
2983
- }
2984
- /** 把终止原因映射成失败描述(fail-closed)。 */
2985
- function rememberFinishError(finish) {
2986
- switch (finish.kind) {
2987
- case "error":
2988
- case "aborted": return `analysis ${finish.kind}: ${errorChain(finish.failure)}`;
2989
- case "max-tokens": return "analysis truncated at the token cap";
2990
- default: return;
2991
- }
2992
- }
2993
- /** 把模型输出的文本拆成一行一条记忆(去前缀、丢空行、丢占位词)。 */
2994
- function splitEntries(blocks) {
2995
- const entries = [];
2996
- for (const block of blocks) {
2997
- if (block.type !== "text" || typeof block.text !== "string") continue;
2998
- for (const raw of block.text.split(/\r?\n/)) {
2999
- const line = stripInvisible(raw.trim().replace(/^[-*]\s+/, "").trim());
3000
- if (line === "") continue;
3001
- if (isVacuousEntry(line)) continue;
3002
- entries.push(line);
3003
- }
2781
+ }));
2782
+ const teardown = () => {
2783
+ disposer();
2784
+ this.toolDisposer = void 0;
2785
+ };
2786
+ ctx.effect(() => teardown, "assistant-profile: memory tool");
2787
+ return () => {
2788
+ disposer();
2789
+ };
3004
2790
  }
3005
- return entries;
3006
- }
2791
+ };
3007
2792
  /** 去掉模型可能在正文周围吐出的不可见字符。 */
3008
2793
  function stripInvisible(value) {
3009
2794
  return value.replace(/[\u0000-\u001F\u007F-\u009F\u00A0\u00AD\u1680\u2000-\u200B\u2028-\u202F\u205F-\u206F\u3000\uFEFF\uFFFD]/g, "").trim();
@@ -3016,4 +2801,4 @@ function isVacuousEntry(line) {
3016
2801
  return normalized === "" || normalized === "空行" || normalized === "无" || normalized === "没有" || normalized === "无内容" || normalized === "无值得记住的内容" || normalized === "暂无" || normalized === "none" || normalized === "no" || normalized === "nothing" || normalized === "na" || normalized === "n/a";
3017
2802
  }
3018
2803
  //#endregion
3019
- export { Config, MEMORY_DIR, MEMORY_FILE, MEMORY_SECTION, appendMemory, dedupeKey, AssistantProfile as default, isVacuousEntry, memoryPath, name, parseEntries, readMemoryText, renderPersona, stampNow, stripInvisible, writeMemory };
2804
+ export { Config, MEMORY_SECTION, appendToText, dedupeKey, AssistantProfile as default, isVacuousEntry, name, parseEntries, renderPersona, stampNow, stripInvisible };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@istarry/dsh-assistant-profile",
3
3
  "description": "助理档案:给 DSH 一份全局人格(名称 / 风格 / 语言 / 人格提示词),每个新会话的系统提示最前面都带着它;侧栏「新会话」上方显示助理名",
4
- "version": "0.4.5",
4
+ "version": "0.5.1",
5
5
  "icon": "./icon.svg",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",
@@ -19,10 +19,6 @@
19
19
  "lib/index.js",
20
20
  "lib/client.js",
21
21
  "lib/client.js.map",
22
- "lib/typert.host.js",
23
- "lib/typert.host.d.ts",
24
- "lib/typert.remote-client.js",
25
- "lib/typert.remote-client.d.ts",
26
22
  "cordis.patch.yml",
27
23
  "LICENSE",
28
24
  "README*.md"
@@ -1,3 +0,0 @@
1
- /* Generated by @deepseek-ai/dsh-typert-generator from FaceModel — do not edit. */
2
-
3
- export declare const TYPERT: unknown