@epoch-agent/core 0.30.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -7265,13 +7265,17 @@ declare function loadPlugins(opts: PluginLoadOptions): PluginArtifacts;
7265
7265
  * | 族 | 谁读它 |
7266
7266
  * | ----------------------------------------------- | ---------------------------------------- |
7267
7267
  * | `name` / `version` / `epochVersion` / `author` | **epoch 自己** —— 装不装得上、叫什么 |
7268
- * | `displayName` / `category` / `visibility` / `changelog` | **上传那一屏的预填**,epoch 一个字不读 |
7268
+ * | `displayName` / `category` / `visibility` / `changelog` | **上传那一屏的预填**(`displayName` 例外,见下) |
7269
7269
  * | `description` / `homepage` / `keywords` / `icon` | 两边都读(`icon` 见它那一格) |
7270
7270
  * | `dependencies` | **epoch 自己** —— 装它时要一起装谁 |
7271
7271
  *
7272
7272
  * 第二族 2026-09-15 加的,判据在它们自己那几格上。要紧的一条:它们是**预填**,
7273
7273
  * 用户在那一屏上改得动,改了之后发出去的是改后的 —— 包里这一份原地不动。
7274
7274
  *
7275
+ * ⚠️ **`displayName` 不只是预填**(这张表原来写的「epoch 一个字不读」早就不成立了):
7276
+ * 安装前那一屏预览和依赖清单读它(`install.ts`),已装那一栏在市场目录说不出名字时
7277
+ * 也拿它兜底(2026-10-14,runtime 的 `PluginEntryView.packageDisplayName`)。
7278
+ *
7275
7279
  * 文件名放在**插件根**,不学竞品的 `.claude-plugin/` 子目录 —— 多一层目录没带来
7276
7280
  * 任何好处,而「插件根下有没有 `epoch-plugin.json`」是判定「这是不是一个插件」
7277
7281
  * 最省事的问法。
@@ -12336,7 +12340,7 @@ declare const EpochConfigSchema: z.ZodObject<{
12336
12340
  id: z.ZodString;
12337
12341
  label: z.ZodString;
12338
12342
  icon: z.ZodOptional<z.ZodString>;
12339
- pinned: z.ZodOptional<z.ZodBoolean>;
12343
+ primary: z.ZodOptional<z.ZodBoolean>;
12340
12344
  }, z.core.$strip>>>;
12341
12345
  userMenu: z.ZodOptional<z.ZodArray<z.ZodObject<{
12342
12346
  id: z.ZodString;
package/dist/index.js CHANGED
@@ -1568,7 +1568,7 @@ async function* runTurnStreaming(opts) {
1568
1568
  if (chunk.warnings) warnings = chunk.warnings;
1569
1569
  break;
1570
1570
  case "error":
1571
- streamError = chunk.error ?? t("agent_run.unknown_provider_error");
1571
+ streamError = chunk.error || t("provider_route.no_detail");
1572
1572
  break;
1573
1573
  case "tool-result":
1574
1574
  break;
@@ -8953,14 +8953,28 @@ function describeBackgroundTask(task) {
8953
8953
  }
8954
8954
  var HEAVY_SOURCE_RATIO = 0.15;
8955
8955
  var HEAVY_SKILL_RATIO = 0.02;
8956
- function seg(label, text, detail) {
8957
- const tokens = text ? estimateTokens(text) : 0;
8958
- if (tokens === 0) return null;
8959
- return { label, tokens, ...detail ? { detail } : {} };
8956
+ var SEG_KEY = {
8957
+ system: "context_breakdown.seg_system",
8958
+ instructions: "context_breakdown.seg_instructions",
8959
+ tools: "context_breakdown.seg_tools",
8960
+ skills: "context_breakdown.seg_skills",
8961
+ memory: "context_breakdown.seg_memory",
8962
+ history: "context_breakdown.seg_history"
8963
+ };
8964
+ var NOTE_KEY = {
8965
+ heavy_mcp: "context_breakdown.heavy_mcp",
8966
+ heavy_tools: "context_breakdown.heavy_tools",
8967
+ heavy_skills: "context_breakdown.heavy_skills",
8968
+ heavy_skills_owner: "context_breakdown.heavy_skills_owner",
8969
+ skills_capped: "context_breakdown.skills_capped",
8970
+ near_limit: "context_breakdown.near_limit"
8971
+ };
8972
+ function seg(kind, text, detail) {
8973
+ return segOf(kind, text ? estimateTokens(text) : 0, detail);
8960
8974
  }
8961
- function segOf(label, tokens, detail) {
8975
+ function segOf(kind, tokens, detail, extra) {
8962
8976
  if (tokens === 0) return null;
8963
- return { label, tokens, ...detail ? { detail } : {} };
8977
+ return { label: t(SEG_KEY[kind]), tokens, kind, ...detail ? { detail } : {}, ...extra };
8964
8978
  }
8965
8979
  function toolDetail(input, toolTokens) {
8966
8980
  const bySource = /* @__PURE__ */ new Map();
@@ -8971,93 +8985,80 @@ function toolDetail(input, toolTokens) {
8971
8985
  bySource.set(source, entry);
8972
8986
  }
8973
8987
  const mcp = [...bySource.entries()].filter(([source]) => source.startsWith("mcp:"));
8974
- const localCount = input.tools.length - mcp.reduce((n, [, e]) => n + e.count, 0);
8975
- const detail = mcp.length > 0 ? t("context_breakdown.tools_mixed", {
8976
- local: localCount,
8977
- mcp: mcp.reduce((n, [, e]) => n + e.count, 0)
8978
- }) : t("context_breakdown.tools_local_only", { local: localCount });
8979
- const hints = [];
8988
+ const mcpCount = mcp.reduce((n, [, e]) => n + e.count, 0);
8989
+ const localCount = input.tools.length - mcpCount;
8990
+ const detail = mcp.length > 0 ? t("context_breakdown.tools_mixed", { local: localCount, mcp: mcpCount }) : t("context_breakdown.tools_local_only", { local: localCount });
8991
+ const notes = [];
8980
8992
  if (input.limit > 0) {
8981
8993
  for (const [source, entry] of mcp) {
8982
8994
  const ratio = entry.tokens / input.limit;
8983
8995
  if (ratio < HEAVY_SOURCE_RATIO) continue;
8984
8996
  const server = source.slice("mcp:".length);
8985
- hints.push(
8986
- t("context_breakdown.heavy_mcp", {
8997
+ notes.push({
8998
+ code: "heavy_mcp",
8999
+ params: {
8987
9000
  server,
8988
9001
  count: entry.count,
8989
9002
  percent: Math.round(ratio * 100),
8990
9003
  tokens: Math.round(entry.tokens / 100) / 10
8991
- })
8992
- );
9004
+ }
9005
+ });
8993
9006
  }
8994
9007
  }
8995
- if (input.limit > 0 && toolTokens / input.limit > 0.3 && hints.length === 0) {
8996
- hints.push(
8997
- t("context_breakdown.heavy_tools", {
8998
- percent: Math.round(toolTokens / input.limit * 100)
8999
- })
9000
- );
9008
+ if (input.limit > 0 && toolTokens / input.limit > 0.3 && notes.length === 0) {
9009
+ notes.push({
9010
+ code: "heavy_tools",
9011
+ params: { percent: Math.round(toolTokens / input.limit * 100) }
9012
+ });
9001
9013
  }
9002
- return { detail, hints };
9014
+ return { detail, counts: { local: localCount, mcp: mcpCount }, notes };
9003
9015
  }
9004
- function skillHints(input, skillTokens) {
9016
+ function skillNotes(input, skillTokens) {
9005
9017
  const stats = input.skillIndex;
9006
9018
  if (!stats || stats.total === 0 || skillTokens === 0) return [];
9007
9019
  if (stats.dropped > 0) {
9008
- return [
9009
- t("context_breakdown.skills_capped", {
9010
- indexed: stats.indexed,
9011
- dropped: stats.dropped
9012
- })
9013
- ];
9020
+ return [{ code: "skills_capped", params: { indexed: stats.indexed, dropped: stats.dropped } }];
9014
9021
  }
9015
- const hints = [];
9022
+ const notes = [];
9016
9023
  if (input.limit > 0 && skillTokens / input.limit >= HEAVY_SKILL_RATIO) {
9017
- hints.push(
9018
- t("context_breakdown.heavy_skills", {
9024
+ notes.push({
9025
+ code: "heavy_skills",
9026
+ params: {
9019
9027
  count: stats.indexed,
9020
9028
  percent: Math.round(skillTokens / input.limit * 100)
9021
- })
9022
- );
9029
+ }
9030
+ });
9023
9031
  if (stats.heaviest) {
9024
- hints.push(
9025
- t("context_breakdown.heavy_skills_owner", {
9026
- owner: stats.heaviest.owner,
9027
- count: stats.heaviest.count
9028
- })
9029
- );
9032
+ notes.push({
9033
+ code: "heavy_skills_owner",
9034
+ params: { owner: stats.heaviest.owner, count: stats.heaviest.count }
9035
+ });
9030
9036
  }
9031
9037
  }
9032
- return hints;
9038
+ return notes;
9033
9039
  }
9034
9040
  function buildContextBreakdown(input) {
9035
9041
  const toolTokens = input.tools.reduce((sum, { tool }) => sum + toolDefinitionTokens(tool), 0);
9036
- const { detail, hints } = toolDetail(input, toolTokens);
9037
- const skillsSeg = seg(t("context_breakdown.seg_skills"), input.system.skills);
9042
+ const { detail, counts, notes } = toolDetail(input, toolTokens);
9043
+ const skillsSeg = seg("skills", input.system.skills);
9038
9044
  const segments = [
9039
- seg("system prompt", `${input.system.identity}
9045
+ seg("system", `${input.system.identity}
9040
9046
  ${input.system.head}
9041
9047
  ${input.system.rules}`),
9042
- seg(
9043
- t("context_breakdown.seg_instructions"),
9044
- input.projectBlock,
9045
- t("context_breakdown.seg_instructions_detail")
9046
- ),
9047
- segOf(t("context_breakdown.seg_tools"), toolTokens, detail),
9048
+ seg("instructions", input.projectBlock, t("context_breakdown.seg_instructions_detail")),
9049
+ segOf("tools", toolTokens, detail, { toolCounts: counts }),
9048
9050
  skillsSeg,
9049
- seg(t("context_breakdown.seg_memory"), input.memoryBlock),
9050
- seg(t("context_breakdown.seg_history"), input.historyText)
9051
+ seg("memory", input.memoryBlock),
9052
+ seg("history", input.historyText)
9051
9053
  ].filter((s) => s !== null);
9052
- hints.push(...skillHints(input, skillsSeg?.tokens ?? 0));
9054
+ notes.push(...skillNotes(input, skillsSeg?.tokens ?? 0));
9053
9055
  const used = segments.reduce((sum, s) => sum + s.tokens, 0);
9054
9056
  const remaining = input.limit > 0 ? Math.max(0, input.limit - used) : 0;
9055
9057
  if (input.limit > 0 && used / input.limit > 0.8) {
9056
- hints.push(
9057
- t("context_breakdown.near_limit", { percent: Math.round(used / input.limit * 100) })
9058
- );
9058
+ notes.push({ code: "near_limit", params: { percent: Math.round(used / input.limit * 100) } });
9059
9059
  }
9060
- return { limit: input.limit, used, remaining, segments, hints };
9060
+ const hints = notes.map((note) => t(NOTE_KEY[note.code], note.params));
9061
+ return { limit: input.limit, used, remaining, segments, hints, notes };
9061
9062
  }
9062
9063
  // src/cost/display.ts
9063
9064
  var currency;
@@ -10738,8 +10739,12 @@ var NETWORK_MESSAGE_RE = /timeout|timed.?out|etimedout|econnreset|enotfound|sock
10738
10739
  function describeProviderError(err) {
10739
10740
  const chain = causeChain(err);
10740
10741
  const head = chain[0]?.message ?? (typeof err === "string" ? err : String(err));
10741
- if (chain.length === 0) return head;
10742
10742
  const root = [...chain].reverse().find((link) => link.message !== "" && link.message !== head)?.message;
10743
+ if (!head.trim()) {
10744
+ if (root) return root;
10745
+ const status = extractStatus(err);
10746
+ return status === void 0 ? t("provider_route.no_detail") : t("provider_route.no_detail_status", { status });
10747
+ }
10743
10748
  return root ? t("provider_route.root_cause", { message: head, cause: root }) : head;
10744
10749
  }
10745
10750
  function extractStatus(err) {
@@ -13096,7 +13101,7 @@ var SidebarMenuItemSchema = z.object({
13096
13101
  icon: z.string().min(1).optional()
13097
13102
  });
13098
13103
  var SidebarNavItemSchema = SidebarMenuItemSchema.extend({
13099
- pinned: z.boolean().optional()
13104
+ primary: z.boolean().optional()
13100
13105
  });
13101
13106
  var BlankCardSchema = z.object({
13102
13107
  id: z.string().min(1),
@@ -13183,6 +13188,8 @@ var EpochConfigSchema = z.object({
13183
13188
  brand: z.string().min(1).optional(),
13184
13189
  sidebarMenu: z.array(SidebarNavItemSchema).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
13185
13190
  message: "sidebarMenu: id must be unique"
13191
+ }).refine((items) => items.filter((item) => item.primary === true).length <= 1, {
13192
+ message: "sidebarMenu: at most one item can be primary"
13186
13193
  }).optional(),
13187
13194
  userMenu: z.array(SidebarMenuItemSchema).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
13188
13195
  message: "userMenu: id must be unique"
@@ -14876,7 +14883,10 @@ var ProviderRouter = class {
14876
14883
  if (next) return next;
14877
14884
  return {
14878
14885
  kind: "give-up",
14879
- message: recovery === "rotate_credential" ? t("provider_route.keys_exhausted") : message,
14886
+ message: recovery === "rotate_credential" ? t("provider_route.root_cause", {
14887
+ message: t("provider_route.credential_rejected"),
14888
+ cause: message
14889
+ }) : message,
14880
14890
  code
14881
14891
  };
14882
14892
  }
@@ -17589,21 +17599,32 @@ var SessionDB = class {
17589
17599
  search(input) {
17590
17600
  const limit = input.limit ?? 5;
17591
17601
  const scope = input.scope ?? "all";
17592
- if (scope === "title") {
17593
- const safe = "%" + sanitizeFtsQuery(input.query) + "%";
17594
- const rows = this.db.prepare(
17595
- `SELECT * FROM sessions WHERE archived = 0 AND title LIKE @q ORDER BY started_at DESC LIMIT @limit`
17596
- ).all({ q: safe, limit });
17597
- return {
17598
- results: rows.map((r) => ({
17599
- session: this.rowToMeta(r),
17600
- matches: [{ messageIndex: -1, snippet: r["title"] ?? "", score: 1 }],
17601
- score: 1
17602
- }))
17603
- };
17604
- }
17605
- const safeQuery = sanitizeFtsQuery(input.query);
17606
- if (!safeQuery) return { results: [] };
17602
+ if (scope === "title") return { results: this.searchTitles(input.query, limit) };
17603
+ const content = this.searchContent(input.query, limit);
17604
+ if (scope === "content") return { results: content };
17605
+ const titles = this.searchTitles(input.query, limit);
17606
+ const seen = new Set(titles.map((r) => r.session.id));
17607
+ return {
17608
+ results: [...titles, ...content.filter((r) => !seen.has(r.session.id))].slice(0, limit)
17609
+ };
17610
+ }
17611
+ searchTitles(query, limit) {
17612
+ const needle = query.trim();
17613
+ if (!needle) return [];
17614
+ const like = "%" + needle.replace(/[\\%_]/g, "\\$&") + "%";
17615
+ const rows = this.db.prepare(
17616
+ `SELECT * FROM sessions WHERE archived = 0 AND title LIKE @q ESCAPE '\\'
17617
+ ORDER BY started_at DESC LIMIT @limit`
17618
+ ).all({ q: like, limit });
17619
+ return rows.map((r) => ({
17620
+ session: this.rowToMeta(r),
17621
+ matches: [{ messageIndex: -1, snippet: r["title"] ?? "", score: 1 }],
17622
+ score: 1
17623
+ }));
17624
+ }
17625
+ searchContent(query, limit) {
17626
+ const safeQuery = sanitizeFtsQuery(query);
17627
+ if (!safeQuery) return [];
17607
17628
  const ftsQuery = buildFtsMatchQuery(safeQuery);
17608
17629
  const ftsRows = this.db.prepare(
17609
17630
  `
@@ -17650,7 +17671,7 @@ var SessionDB = class {
17650
17671
  score: 1
17651
17672
  });
17652
17673
  }
17653
- return { results: results2 };
17674
+ return results2;
17654
17675
  }
17655
17676
  const seen = /* @__PURE__ */ new Set();
17656
17677
  const results = [];
@@ -17671,7 +17692,7 @@ var SessionDB = class {
17671
17692
  score: 1
17672
17693
  });
17673
17694
  }
17674
- return { results };
17695
+ return results;
17675
17696
  }
17676
17697
  pruneCandidates(input) {
17677
17698
  const clauses = ["ended_at IS NOT NULL", "updated_at < @cutoff", "ended_at < @cutoff"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epoch-agent/core",
3
- "version": "0.30.0",
3
+ "version": "1.0.0",
4
4
  "private": false,
5
5
  "description": "epoch-agent 核心引擎:ReAct 循环、Provider 路由、工具调度、记忆管理",
6
6
  "repository": {
@@ -27,8 +27,8 @@
27
27
  "gpt-tokenizer": "^3.4.0",
28
28
  "js-yaml": "^5.2.3",
29
29
  "zod": "^4.4.3",
30
- "@epoch-agent/infra": "0.30.0",
31
- "@epoch-agent/protocol": "0.30.0"
30
+ "@epoch-agent/infra": "1.0.0",
31
+ "@epoch-agent/protocol": "1.0.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@ai-sdk/alibaba": "^2.0.59",