@epoch-agent/server 0.14.0 → 0.15.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
@@ -3044,6 +3044,19 @@ interface MarketplaceView {
3044
3044
  entries: number;
3045
3045
  addedAt: number;
3046
3046
  local: boolean;
3047
+ /** 这个市场声明的分类层级(2026-09-21)。缺席 = 它没声明,判据在 runtime 那一格上 */
3048
+ categories?: readonly CategoryView[];
3049
+ }
3050
+ /**
3051
+ * `MarketplaceCategory`(core,经 runtime 原样透出)的镜像。
3052
+ *
3053
+ * ⚠️ **两格都可选**,和网线上那一份刻意不同形:补齐是这一层的活
3054
+ * ({@link toWireCategory}),而镜像要如实描述**收到的**是什么。
3055
+ */
3056
+ interface CategoryView {
3057
+ id: string;
3058
+ label?: string;
3059
+ children?: readonly CategoryView[];
3047
3060
  }
3048
3061
  /** 加 / 移除一个市场的结局。移除成功那一档没有 `marketplace` —— 它已经不在表里了 */
3049
3062
  type MarketOutcomeView = {
@@ -4413,7 +4426,10 @@ declare function unavailableToHttp(lang?: Lang): {
4413
4426
  * 两边都可能单独存在,所以两边都不能当成必要条件:
4414
4427
  *
4415
4428
  * - **只有 Hub 有**:会话刚建出来、一条消息都还没落盘。DB 那半全 null,
4416
- * 但它必须出现在列表里 —— 否则用户新建一个会话,列表上什么都没有
4429
+ * 但它必须出现在列表里 —— 否则用户新建一个会话,列表上什么都没有。
4430
+ * ⚠️ 2026-09-20 起这一条有**唯一一个例外**:没人用过的那段引导会话
4431
+ * (判据在 `listSessionSummaries` 的 `unusedBootstrapId` 和下面那条 `fresh`
4432
+ * 通道上)。例外只此一个,而且划得掉的只有「Hub 里有、DB 里没有」那一半
4417
4433
  * - **只有 DB 有**:上次进程留下的会话,或者本进程里被冷却掉的。它照样要出现,
4418
4434
  * `live: false` 说明「Hub 手里没有它」,重新打开时走 `GET /messages` 回放
4419
4435
  *
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createServer } from 'http';
2
- import { createLogger, t, ApprovalRelay, QuestionRelay, serializeStream, withRoleScope, withModelScope, withToolScope, nextRunAt, observeBackgroundTasks, skillIndexDescription, SANDBOX_EXCLUDES, SANDBOX_COVERS, guardSpawn, isSystemOpenable, backgroundTaskOutput, listBackgroundTasks, providerLabel, MAX_REASON_CHARS, MAX_OBJECTIVE_CHARS, MAX_GOAL_MAX_ROUNDS, DEFAULT_GOAL_MAX_ROUNDS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_FORM_BUDGET_USD, SCHEDULE_DEFAULTS, coalesceRecording, readRecording } from '@epoch-agent/runtime';
2
+ import { createLogger, t, ApprovalRelay, QuestionRelay, serializeStream, withRoleScope, withModelScope, withToolScope, nextRunAt, observeBackgroundTasks, skillIndexDescription, SANDBOX_EXCLUDES, SANDBOX_COVERS, canProbeGit, guardSpawn, isSystemOpenable, backgroundTaskOutput, listBackgroundTasks, providerLabel, MAX_REASON_CHARS, MAX_OBJECTIVE_CHARS, MAX_GOAL_MAX_ROUNDS, DEFAULT_GOAL_MAX_ROUNDS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_FORM_BUDGET_USD, SCHEDULE_DEFAULTS, coalesceRecording, readRecording } from '@epoch-agent/runtime';
3
3
  import { timingSafeEqual, randomBytes } from 'crypto';
4
4
  import { PLAN_OUTCOMES, WIRE_AUTH_COOKIE, WIRE_AUTH_COOKIE_ATTRS, WIRE_AUTH_TOKEN_PARAM, extractAllMentions, MAX_ATTACH_FILES, utf8ByteLength, MAX_SESSION_REFS, attachSessionSurface, isWireRewindScope, WIRE_REWIND_SCOPES, mcpConnectorState, MAX_ATTACH_TOTAL_BYTES, MAX_ATTACH_BYTES, truncateToBudget, WIRE_LANG_PARAM, isLang, conformsToWire, WIRE_LAST_SESSION_FIELDS, isQuestionAnswerMap, isProviderType, apiKeyEnvVar, WIRE_FILE_STAT_MAX, WIRE_SETTING_WRITE_LAYERS, isWirePlanAction, WIRE_PLAN_ACTIONS, isPermissionLevel, PERMISSION_LEVELS, WIRE_TRUST_REQUEST_FIELDS, WIRE_FORGET_WORKSPACE_FIELDS, collectPendingApprovals, unfixedRules, isWireSettingWriteLayer, parseModelRef, WIRE_GOAL_ACTIONS, OPERATION_TYPES, isOperationType, WIRE_PLUGIN_UPLOAD_FORM_FIELDS } from '@epoch-agent/protocol';
5
5
  import { resolve, join, relative, isAbsolute, sep, dirname, basename, extname } from 'path';
@@ -390,7 +390,7 @@ function toWireSummaryWorkspace(bound, live, row) {
390
390
  }
391
391
  return row?.workspaceState === "none" ? { state: "none" } : { state: "unknown" };
392
392
  }
393
- function listSessionSummaries(hubStates, catalog, query, workspaceOf = () => null, roleOf = () => null, limit = DEFAULT_SESSION_LIST_LIMIT, archived = false) {
393
+ function listSessionSummaries(hubStates, catalog, query, workspaceOf = () => null, roleOf = () => null, limit = DEFAULT_SESSION_LIST_LIMIT, archived = false, unusedBootstrapId = null) {
394
394
  const byId = new Map(hubStates.map((s) => [s.id, s]));
395
395
  if (query) {
396
396
  if (!catalog) return [];
@@ -417,7 +417,7 @@ function listSessionSummaries(hubStates, catalog, query, workspaceOf = () => nul
417
417
  })
418
418
  );
419
419
  const known = new Set(rows.map((r) => r.id));
420
- const fresh = archived ? [] : hubStates.filter((s) => !known.has(s.id)).filter((s) => catalog?.get(s.id)?.archived !== true).map(
420
+ const fresh = archived ? [] : hubStates.filter((s) => !known.has(s.id)).filter((s) => catalog?.get(s.id)?.archived !== true).filter((s) => s.id !== unusedBootstrapId).map(
421
421
  (s) => toWireSummary(s.id, s, null, { workspace: workspaceOf(s.id), role: roleOf(s.id) })
422
422
  );
423
423
  return [...fresh, ...summaries].sort(compareSummaries).slice(0, limit);
@@ -1689,10 +1689,16 @@ function listSessions(ctx, res, query, _lang, archived = false) {
1689
1689
  (id) => ctx.runtime.workspaces.of(id),
1690
1690
  (id) => ctx.runtime.sessionFactory.get(id)?.role ?? null,
1691
1691
  void 0,
1692
- archived
1692
+ archived,
1693
+ unusedBootstrap(ctx)
1693
1694
  );
1694
1695
  sendJson(res, 200, { sessions });
1695
1696
  }
1697
+ function unusedBootstrap(ctx) {
1698
+ const { lastSessionId } = lastSessionField(ctx);
1699
+ if (!lastSessionId || lastSessionId === ctx.runtime.sessionId) return null;
1700
+ return ctx.runtime.sessionId;
1701
+ }
1696
1702
  function getSession(ctx, res, sessionId, lang) {
1697
1703
  const hub = ctx.hub.listSessions().find((one) => one.id === sessionId);
1698
1704
  const row = ctx.runtime.sessions?.get(sessionId) ?? null;
@@ -2764,7 +2770,15 @@ function toWireMarketplace(market) {
2764
2770
  source: market.source,
2765
2771
  entries: market.entries,
2766
2772
  addedAt: market.addedAt,
2767
- local: market.local
2773
+ local: market.local,
2774
+ categories: (market.categories ?? []).map(toWireCategory)
2775
+ };
2776
+ }
2777
+ function toWireCategory(node) {
2778
+ return {
2779
+ id: node.id,
2780
+ label: node.label ?? node.id,
2781
+ children: (node.children ?? []).map(toWireCategory)
2768
2782
  };
2769
2783
  }
2770
2784
  function toWirePreview2(preview) {
@@ -4443,6 +4457,7 @@ async function forgetWorkspace(ctx, sessionId, req, res, lang) {
4443
4457
  var GIT_TIMEOUT_MS = 1e4;
4444
4458
  var GIT_MAX_BUFFER = 16 * 1024 * 1024;
4445
4459
  function runGit(cwd, args) {
4460
+ if (!canProbeGit()) return Promise.resolve({ ok: false, error: "git unavailable" });
4446
4461
  return new Promise((resolve4) => {
4447
4462
  guardSpawn(
4448
4463
  () => execFile(