@epoch-agent/server 0.7.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/README.md
CHANGED
|
@@ -254,8 +254,17 @@ sessionId 就是方案 30 §9.5 那条 bug 的形状 —— 编译得过、跑
|
|
|
254
254
|
|
|
255
255
|
**录像那一条不再脱敏一次**:脱敏在落盘那一刻就做完了(core 的 `redactLine`,
|
|
256
256
|
跑在已经序列化过的一整行上)。在这一层补一道会让下一个人以为盘上那一份是干净的
|
|
257
|
-
—— 而盘上那一份才是真正会被 `cat`
|
|
258
|
-
|
|
257
|
+
—— 而盘上那一份才是真正会被 `cat` 的那份。
|
|
258
|
+
|
|
259
|
+
发出去之前**先把流式增量折回消息**(core 的 `coalesceRecording`):盘上是一个
|
|
260
|
+
`text-delta` 一帧,几千个输出 token 就是几千帧,而这些帧在运行结束之后没有独立
|
|
261
|
+
含义(渲染那侧只是把它们拼成一段正文)。折完之后一帧 ≈ 一段正文 / 一次工具调用,
|
|
262
|
+
`MAX_RECORDING_FRAMES`(4000)这把尺子才量得到真东西 —— **它量的是折叠之后的帧数**。
|
|
263
|
+
|
|
264
|
+
超了就截断 + `truncated: true`,界面必须把它画出来。⚠️ **截掉的是开头,留的是
|
|
265
|
+
尾巴**(`slice(-N)`):一次运行里人要看的正是那句结论和 `finish` 那一帧上的账,
|
|
266
|
+
而留头去尾会让正文断在半句话上 —— 屏幕上那半截自己不会说「我是半截的」
|
|
267
|
+
(判据同 protocol 的 `NoticeCode: 'response-truncated'`)。
|
|
259
268
|
|
|
260
269
|
⚠️ 这一组借的是 `WebRuntimeView.schedules`(`ScheduleControlView`,
|
|
261
270
|
结构镜像在 [src/schedule/view.ts](./src/schedule/view.ts))。判定、注册进 OS、
|
package/dist/index.d.ts
CHANGED
|
@@ -2666,8 +2666,26 @@ interface ScheduleCreateView {
|
|
|
2666
2666
|
endDate?: string;
|
|
2667
2667
|
enabled?: boolean;
|
|
2668
2668
|
}
|
|
2669
|
-
/**
|
|
2670
|
-
|
|
2669
|
+
/**
|
|
2670
|
+
* 改一条任务 —— core 的 `UpdateScheduleInput`。没给的字段一个都不动。
|
|
2671
|
+
*
|
|
2672
|
+
* ⚠️ **四格收 `null` = 清空这一格**,和网线上那份(`WireScheduleUpdateRequest`)
|
|
2673
|
+
* 逐字段对得上。别写成 `Partial<ScheduleCreateView>`(2026-09-07 之前就是那样):
|
|
2674
|
+
* 那一版比真身**窄**,而窄的方向 TS 不报 —— 入参是逆变的,一个「什么都收得下」
|
|
2675
|
+
* 的真身赋给一个「只收 string」的镜像永远合法。于是这一层压根表达不出「清空」,
|
|
2676
|
+
* `schedule/body.ts` 只好把 `null` 翻成空串往下递,而 core 从来不认空串是清空。
|
|
2677
|
+
* 那次的表现是:表单上两格生效区间都没填,保存却报两行「生效区间不对: 」。
|
|
2678
|
+
*
|
|
2679
|
+
* 也就是说**这一格的正确性没有编译器守着**,只有这段注释和用例。判据全文在
|
|
2680
|
+
* core 的 `UpdateScheduleInput` 上。
|
|
2681
|
+
*/
|
|
2682
|
+
type ScheduleUpdateView = Partial<Omit<ScheduleCreateView, 'workDir' | 'model' | 'startDate' | 'endDate'>> & {
|
|
2683
|
+
/** `null` = 不绑工作区了 → 落回 `~/.epoch/automation/<id>/`(runtime 那一层解析) */
|
|
2684
|
+
workDir?: string | null;
|
|
2685
|
+
model?: string | null;
|
|
2686
|
+
startDate?: string | null;
|
|
2687
|
+
endDate?: string | null;
|
|
2688
|
+
};
|
|
2671
2689
|
/** 存下来了没有 —— core / runtime 的 `ScheduleSaveResult` */
|
|
2672
2690
|
interface ScheduleSaveView {
|
|
2673
2691
|
/** 校验不过时 `undefined`,且库里一个字都没写 */
|
|
@@ -4884,12 +4902,17 @@ declare function collectTasks(sessionId: string, registry: TaskRegistryView): Wi
|
|
|
4884
4902
|
*/
|
|
4885
4903
|
|
|
4886
4904
|
/**
|
|
4887
|
-
*
|
|
4905
|
+
* 一份录像最多回多少帧 —— **折叠之后的帧**。
|
|
4906
|
+
*
|
|
4907
|
+
* 一次跑飞的运行能写出几万帧,整份推给浏览器会让那个标签页卡死。超了就截断 +
|
|
4908
|
+
* `truncated: true` —— **必须说出来**,判据同 `WireSkillBodyResponse.truncated`:
|
|
4909
|
+
* 一份看起来完整、其实少了后半截的回放,比一句「太长了没给全」坏得多。
|
|
4888
4910
|
*
|
|
4889
|
-
*
|
|
4890
|
-
*
|
|
4891
|
-
*
|
|
4892
|
-
*
|
|
4911
|
+
* ⚠️ **这把尺子量的是折叠之后的帧数**(2026-09-07 改准)。原来量的是盘上的原始
|
|
4912
|
+
* 帧数,而盘上「每个 `text-delta` 一帧」—— 于是几千个输出 token 就是几千帧,
|
|
4913
|
+
* **一次正常的多轮任务**(实测:27 轮、0.016 美元)就撞上了这道闸门。
|
|
4914
|
+
* 折叠在 core 的 `coalesceRecording` 里,折完一帧 ≈ 一段正文 / 一次工具调用,
|
|
4915
|
+
* 4000 才重新是「一次真正巨大的运行」的意思。
|
|
4893
4916
|
*/
|
|
4894
4917
|
declare const MAX_RECORDING_FRAMES = 4000;
|
|
4895
4918
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createServer } from 'http';
|
|
2
|
-
import { t, ApprovalRelay, QuestionRelay, serializeStream, withRoleScope, withModelScope, withToolScope, nextRunAt, skillIndexDescription, SANDBOX_EXCLUDES, SANDBOX_COVERS, isSystemOpenable, backgroundTaskOutput, listBackgroundTasks, providerLabel, MAX_REASON_CHARS, MAX_OBJECTIVE_CHARS, MAX_GOAL_MAX_ROUNDS, DEFAULT_GOAL_MAX_ROUNDS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_DEFAULTS, readRecording } from '@epoch-agent/runtime';
|
|
2
|
+
import { t, ApprovalRelay, QuestionRelay, serializeStream, withRoleScope, withModelScope, withToolScope, nextRunAt, skillIndexDescription, SANDBOX_EXCLUDES, SANDBOX_COVERS, isSystemOpenable, backgroundTaskOutput, listBackgroundTasks, providerLabel, MAX_REASON_CHARS, MAX_OBJECTIVE_CHARS, MAX_GOAL_MAX_ROUNDS, DEFAULT_GOAL_MAX_ROUNDS, SCHEDULE_DEFAULT_ALLOWLIST, 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, MAX_ATTACH_TOTAL_BYTES, MAX_ATTACH_BYTES, WIRE_LANG_PARAM, isLang, isQuestionAnswerMap, isProviderType, apiKeyEnvVar, WIRE_FILE_STAT_MAX, WIRE_SETTING_WRITE_LAYERS, isWirePlanAction, WIRE_PLAN_ACTIONS, isPermissionLevel, PERMISSION_LEVELS, collectPendingApprovals, unfixedRules, isWireSettingWriteLayer, parseModelRef, WIRE_GOAL_ACTIONS, OPERATION_TYPES, isOperationType } from '@epoch-agent/protocol';
|
|
5
5
|
import { resolve, join, relative, isAbsolute, sep, dirname, basename, extname } from 'path';
|
|
@@ -2855,21 +2855,28 @@ function numberList(value) {
|
|
|
2855
2855
|
}
|
|
2856
2856
|
return out;
|
|
2857
2857
|
}
|
|
2858
|
-
|
|
2858
|
+
var CLEARABLE = ["model", "workDir", "startDate", "endDate"];
|
|
2859
|
+
function optionals(raw, allowClear, lang) {
|
|
2859
2860
|
const out = {};
|
|
2860
|
-
|
|
2861
|
+
const bad = (field) => ({
|
|
2862
|
+
ok: false,
|
|
2863
|
+
message: t("web.schedule_bad_field", { field }, lang)
|
|
2864
|
+
});
|
|
2865
|
+
for (const key of ["name", "prompt"]) {
|
|
2866
|
+
const value = raw[key];
|
|
2867
|
+
if (value === void 0) continue;
|
|
2868
|
+
if (typeof value !== "string") return bad(key);
|
|
2869
|
+
out[key] = value;
|
|
2870
|
+
}
|
|
2871
|
+
for (const key of CLEARABLE) {
|
|
2861
2872
|
const value = raw[key];
|
|
2862
2873
|
if (value === void 0) continue;
|
|
2863
2874
|
if (value === null) {
|
|
2864
|
-
if (
|
|
2865
|
-
|
|
2866
|
-
}
|
|
2867
|
-
out[key] = "";
|
|
2875
|
+
if (!allowClear) return bad(key);
|
|
2876
|
+
out[key] = null;
|
|
2868
2877
|
continue;
|
|
2869
2878
|
}
|
|
2870
|
-
if (typeof value !== "string")
|
|
2871
|
-
return { ok: false, message: t("web.schedule_bad_field", { field: key }, lang) };
|
|
2872
|
-
}
|
|
2879
|
+
if (typeof value !== "string") return bad(key);
|
|
2873
2880
|
out[key] = value;
|
|
2874
2881
|
}
|
|
2875
2882
|
for (const key of ["maxTurns", "maxBudgetUsd", "timeoutMs"]) {
|
|
@@ -2939,7 +2946,7 @@ function parseCreate2(body, lang) {
|
|
|
2939
2946
|
return { ok: false, message: t("web.schedule_bad_trigger", void 0, lang) };
|
|
2940
2947
|
if (budget === null)
|
|
2941
2948
|
return { ok: false, message: t("web.schedule_budget_required", void 0, lang) };
|
|
2942
|
-
const rest = optionals(raw, lang);
|
|
2949
|
+
const rest = optionals(raw, false, lang);
|
|
2943
2950
|
if (!rest.ok) return rest;
|
|
2944
2951
|
return {
|
|
2945
2952
|
ok: true,
|
|
@@ -2947,7 +2954,7 @@ function parseCreate2(body, lang) {
|
|
|
2947
2954
|
};
|
|
2948
2955
|
}
|
|
2949
2956
|
function parseUpdate2(body, lang) {
|
|
2950
|
-
return optionals(asRecord(body), lang);
|
|
2957
|
+
return optionals(asRecord(body), true, lang);
|
|
2951
2958
|
}
|
|
2952
2959
|
function parseFix(body, lang) {
|
|
2953
2960
|
const runId = asRecord(body)["runId"];
|
|
@@ -3105,14 +3112,14 @@ function toRecording(run) {
|
|
|
3105
3112
|
if (run.logPath === void 0) {
|
|
3106
3113
|
return { runId: run.id, frames: [], missing: true, truncated: false };
|
|
3107
3114
|
}
|
|
3108
|
-
const frames = readRecording(run.logPath);
|
|
3115
|
+
const frames = coalesceRecording(readRecording(run.logPath));
|
|
3109
3116
|
if (frames.length === 0) {
|
|
3110
3117
|
return { runId: run.id, frames: [], missing: true, truncated: false };
|
|
3111
3118
|
}
|
|
3112
3119
|
const truncated = frames.length > MAX_RECORDING_FRAMES;
|
|
3113
3120
|
return {
|
|
3114
3121
|
runId: run.id,
|
|
3115
|
-
frames: truncated ? frames.slice(
|
|
3122
|
+
frames: truncated ? frames.slice(-MAX_RECORDING_FRAMES) : frames,
|
|
3116
3123
|
missing: false,
|
|
3117
3124
|
truncated
|
|
3118
3125
|
};
|