@botlearn-course/daemon 0.0.13 → 0.0.14
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 +5 -2
- package/dist/agent-service-sandbox.d.ts +4 -0
- package/dist/agent-service-sandbox.js +74 -19
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/course-skills-relay.d.ts +2 -0
- package/dist/mcp/course-skills-relay.js +54 -0
- package/dist/mcp/course-skills-server.d.ts +49 -0
- package/dist/mcp/course-skills-server.js +439 -0
- package/dist/run-dispatcher.d.ts +2 -0
- package/dist/run-dispatcher.js +61 -2
- package/dist/runtime-capabilities.d.ts +1 -0
- package/dist/runtime-capabilities.js +4 -0
- package/dist/runtime-skills.d.ts +93 -0
- package/dist/runtime-skills.js +401 -0
- package/dist/runtimes/deepseek-tui.d.ts +2 -1
- package/dist/runtimes/deepseek-tui.js +252 -25
- package/dist/runtimes/engine.d.ts +6 -0
- package/dist/runtimes/engine.js +6 -1
- package/dist/runtimes/progress.d.ts +2 -0
- package/dist/runtimes/progress.js +34 -0
- package/dist/tool-observation.d.ts +21 -0
- package/dist/tool-observation.js +120 -0
- package/dist/types.d.ts +26 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,8 +114,11 @@ runtime 凭据。
|
|
|
114
114
|
- 本包 production 依赖为零,发布 tarball 只包含 `dist/`、`README.md`、`package.json`、
|
|
115
115
|
`LICENSE`(CI 发布前强制校验)。
|
|
116
116
|
- 托管 session 的 control token/reconnect state 位于 runtime UID 不可访问的 `0700` control home;
|
|
117
|
-
runtime workspace
|
|
118
|
-
|
|
117
|
+
runtime workspace 独立可写。activation-scoped Skill Provider binding 只留在 daemon control
|
|
118
|
+
process 内存;DeepSeek 只通过 clean-env `course_skills` Unix Socket relay 按需读取当前授权
|
|
119
|
+
`SKILL.md`/reference,endpoint/token 不进入 runtime env、MCP config、state、workspace 或日志。
|
|
120
|
+
Prompt Pack 仍通过受信 system context 应用。长期模型 provider key 留在 Agent Service,runtime
|
|
121
|
+
只拿 generation-scoped proxy grant。
|
|
119
122
|
|
|
120
123
|
## 发布
|
|
121
124
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Logger } from "./log.js";
|
|
2
|
+
import { type RuntimeSkillProviderFactory } from "./runtime-skills.js";
|
|
2
3
|
import { type PersistentSessionExecution, type PreparedPersistentTurn, type RunReportingClient } from "./run-dispatcher.js";
|
|
3
4
|
import type { CourseRuntime, CourseRuntimeProfile, RunEvent, RunFileCandidate, RunFileRecord, RunStartPayload } from "./types.js";
|
|
4
5
|
export interface AgentServiceSandboxOptions {
|
|
@@ -11,6 +12,8 @@ export interface AgentServiceSandboxOptions {
|
|
|
11
12
|
log?: Logger;
|
|
12
13
|
random?: () => number;
|
|
13
14
|
sleep?: (ms: number) => Promise<void>;
|
|
15
|
+
/** Test/provider injection; production uses the bounded HTTP Runtime Skill Provider. */
|
|
16
|
+
prepareSkillProvider?: RuntimeSkillProviderFactory;
|
|
14
17
|
}
|
|
15
18
|
/**
|
|
16
19
|
* Long-running daemon client for one user-scoped managed sandbox (ADR-015).
|
|
@@ -24,6 +27,7 @@ export declare class AgentServiceSandboxClient implements RunReportingClient, Pe
|
|
|
24
27
|
private readonly log;
|
|
25
28
|
private readonly random;
|
|
26
29
|
private readonly sleep;
|
|
30
|
+
private readonly prepareSkillProvider;
|
|
27
31
|
private readonly state;
|
|
28
32
|
private readonly dispatcher;
|
|
29
33
|
/** agent_run_id → TURN scope(session/attempt/activation),事件与文件帧路由用。 */
|
|
@@ -3,9 +3,10 @@ import path from "node:path";
|
|
|
3
3
|
import { ensureDaemonHome } from "./auth-store.js";
|
|
4
4
|
import { AGENT_SERVICE_WS_SCHEMA, AGENT_SERVICE_WS_SUBPROTOCOL, createSandboxFrame, parseSandboxFrame, UnsupportedSandboxProtocolError, } from "./agent-service-ws-protocol.js";
|
|
5
5
|
import { log as defaultLog } from "./log.js";
|
|
6
|
-
import { availableRunCapabilities } from "./runtime-capabilities.js";
|
|
6
|
+
import { availableRunCapabilities, runtimeSupportsCourseSkills, } from "./runtime-capabilities.js";
|
|
7
7
|
import { activationRuntimeEnv, runtimeChildEnv } from "./runtime-env.js";
|
|
8
8
|
import { redactSecretString } from "./redaction.js";
|
|
9
|
+
import { parseRuntimeSkillProviderGrantSet, prepareRuntimeSkillProvider, RuntimeSkillProviderError, } from "./runtime-skills.js";
|
|
9
10
|
import { RunDispatcher, } from "./run-dispatcher.js";
|
|
10
11
|
import { ensureRuntimeSessionDirectories, ensureRuntimeSessionWorkspace, exposeRuntimeSessionWorkspace, removeRuntimeSessionWorkspace, revokeRuntimeSessionWorkspace, } from "./workspace.js";
|
|
11
12
|
import { WebSocketClient, } from "./websocket-client.js";
|
|
@@ -44,6 +45,9 @@ function inputAttachmentGrant(value) {
|
|
|
44
45
|
}
|
|
45
46
|
return { baseUrl: parsed.toString().replace(/\/$/, ""), token };
|
|
46
47
|
}
|
|
48
|
+
function runtimeSkillGrantKey(grants) {
|
|
49
|
+
return JSON.stringify(grants);
|
|
50
|
+
}
|
|
47
51
|
class SandboxClosedError extends Error {
|
|
48
52
|
code;
|
|
49
53
|
reason;
|
|
@@ -244,6 +248,7 @@ export class AgentServiceSandboxClient {
|
|
|
244
248
|
log;
|
|
245
249
|
random;
|
|
246
250
|
sleep;
|
|
251
|
+
prepareSkillProvider;
|
|
247
252
|
state;
|
|
248
253
|
dispatcher;
|
|
249
254
|
/** agent_run_id → TURN scope(session/attempt/activation),事件与文件帧路由用。 */
|
|
@@ -277,6 +282,8 @@ export class AgentServiceSandboxClient {
|
|
|
277
282
|
this.log = options.log ?? defaultLog;
|
|
278
283
|
this.random = options.random ?? Math.random;
|
|
279
284
|
this.sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
285
|
+
this.prepareSkillProvider =
|
|
286
|
+
options.prepareSkillProvider ?? ((raw) => prepareRuntimeSkillProvider(raw));
|
|
280
287
|
this.state = loadState(options.sandboxId, options.sandboxToken);
|
|
281
288
|
this.dispatcher = new RunDispatcher(this, options.runtimes, {
|
|
282
289
|
persistentSession: this,
|
|
@@ -318,6 +325,7 @@ export class AgentServiceSandboxClient {
|
|
|
318
325
|
contextRevision,
|
|
319
326
|
runtimeEnv,
|
|
320
327
|
inputAttachmentGrant: activation.inputAttachmentGrant,
|
|
328
|
+
...(activation.skillProvider ? { skillProvider: activation.skillProvider } : {}),
|
|
321
329
|
};
|
|
322
330
|
}
|
|
323
331
|
persistNativeSession(nativeSessionId) {
|
|
@@ -849,33 +857,59 @@ export class AgentServiceSandboxClient {
|
|
|
849
857
|
await this.sendCommandAck(frame, "rejected", error instanceof Error ? error.message : "invalid_instructions");
|
|
850
858
|
return;
|
|
851
859
|
}
|
|
860
|
+
const rawSkillProvider = frame.payload.skill_provider;
|
|
861
|
+
const hasSkillProvider = rawSkillProvider !== undefined && rawSkillProvider !== null;
|
|
862
|
+
if (hasSkillProvider && !runtimeSupportsCourseSkills(runtimeId)) {
|
|
863
|
+
await this.sendCommandAck(frame, "rejected", "skill_provider_runtime_unsupported");
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
let skillGrantKey;
|
|
867
|
+
if (hasSkillProvider) {
|
|
868
|
+
try {
|
|
869
|
+
skillGrantKey = runtimeSkillGrantKey(parseRuntimeSkillProviderGrantSet(rawSkillProvider));
|
|
870
|
+
}
|
|
871
|
+
catch (error) {
|
|
872
|
+
const code = error instanceof RuntimeSkillProviderError
|
|
873
|
+
? error.code
|
|
874
|
+
: "skill_provider_binding_invalid";
|
|
875
|
+
await this.sendCommandAck(frame, "rejected", code);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
852
879
|
const capabilities = Array.isArray(frame.payload.capabilities)
|
|
853
880
|
? frame.payload.capabilities.filter((item) => typeof item === "string" && item.length > 0)
|
|
854
881
|
: [];
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
882
|
+
let availableCapabilities;
|
|
883
|
+
const missingCapabilities = (required) => {
|
|
884
|
+
if (required.length === 0)
|
|
885
|
+
return [];
|
|
886
|
+
if (!availableCapabilities) {
|
|
887
|
+
const workspace = ensureRuntimeSessionDirectories(sessionId, this.sandboxGeneration);
|
|
888
|
+
availableCapabilities = new Set(availableRunCapabilities({
|
|
889
|
+
agent_run_id: "activation-probe",
|
|
890
|
+
course_run_id: session.courseRunId ?? "",
|
|
891
|
+
lesson_id: null,
|
|
892
|
+
task_id: null,
|
|
893
|
+
agent_instance_id: null,
|
|
894
|
+
runtime: { id: runtimeId },
|
|
895
|
+
input: {},
|
|
896
|
+
context: {},
|
|
897
|
+
limits: {},
|
|
898
|
+
}, workspace.workspaceDir));
|
|
872
899
|
}
|
|
900
|
+
return Array.from(new Set(required.filter((item) => !availableCapabilities.has(item)))).sort();
|
|
901
|
+
};
|
|
902
|
+
const missingDeclaredCapabilities = missingCapabilities(capabilities);
|
|
903
|
+
if (missingDeclaredCapabilities.length > 0) {
|
|
904
|
+
await this.sendCommandAck(frame, "rejected", `missing_capabilities:${missingDeclaredCapabilities.join(",")}`);
|
|
905
|
+
return;
|
|
873
906
|
}
|
|
874
907
|
const existingActivation = this.activationContexts.get(sessionId);
|
|
875
908
|
if (existingActivation?.activationId === activationId) {
|
|
876
909
|
if (this.activeSessionId !== sessionId ||
|
|
877
910
|
session.runtimeId !== runtimeId ||
|
|
878
|
-
session.contextRevision !== contextRevision
|
|
911
|
+
session.contextRevision !== contextRevision ||
|
|
912
|
+
existingActivation.skillGrantKey !== skillGrantKey) {
|
|
879
913
|
await this.sendCommandAck(frame, "rejected", "activation_redefinition");
|
|
880
914
|
return;
|
|
881
915
|
}
|
|
@@ -891,6 +925,25 @@ export class AgentServiceSandboxClient {
|
|
|
891
925
|
await this.sendCommandAck(frame, "ok");
|
|
892
926
|
return;
|
|
893
927
|
}
|
|
928
|
+
let skillProvider;
|
|
929
|
+
if (hasSkillProvider) {
|
|
930
|
+
try {
|
|
931
|
+
skillProvider = await this.prepareSkillProvider(rawSkillProvider);
|
|
932
|
+
}
|
|
933
|
+
catch (error) {
|
|
934
|
+
const code = error instanceof RuntimeSkillProviderError
|
|
935
|
+
? error.code
|
|
936
|
+
: "skill_provider_prepare_failed";
|
|
937
|
+
await this.sendCommandAck(frame, "rejected", code);
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
const requiredBySkills = skillProvider.catalog.flatMap((entry) => entry.requiredCapabilities);
|
|
941
|
+
const missingSkillCapabilities = missingCapabilities(requiredBySkills);
|
|
942
|
+
if (missingSkillCapabilities.length > 0) {
|
|
943
|
+
await this.sendCommandAck(frame, "rejected", `missing_capabilities:${missingSkillCapabilities.join(",")}`);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
894
947
|
// 同一时刻只允许一个 active session/activation。先撤销上一 workspace 的
|
|
895
948
|
// runtime 访问并等待进程退出,再暴露新 workspace,避免同 UID 跨 Session 读取。
|
|
896
949
|
const previousSessionId = this.activeSessionId;
|
|
@@ -920,6 +973,8 @@ export class AgentServiceSandboxClient {
|
|
|
920
973
|
runtimeEnv,
|
|
921
974
|
instructions,
|
|
922
975
|
inputAttachmentGrant: attachmentGrant,
|
|
976
|
+
...(skillProvider ? { skillProvider } : {}),
|
|
977
|
+
...(skillGrantKey ? { skillGrantKey } : {}),
|
|
923
978
|
});
|
|
924
979
|
this.activeSessionId = sessionId;
|
|
925
980
|
this.persist();
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./log.js";
|
|
|
18
18
|
export * from "./redaction.js";
|
|
19
19
|
export * from "./runtime-profile.js";
|
|
20
20
|
export * from "./runtime-env.js";
|
|
21
|
+
export * from "./runtime-skills.js";
|
|
21
22
|
export * from "./mcp/report-progress.js";
|
|
22
23
|
export * from "./runtimes/index.js";
|
|
23
24
|
export * from "./runtimes/engine.js";
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./log.js";
|
|
|
18
18
|
export * from "./redaction.js";
|
|
19
19
|
export * from "./runtime-profile.js";
|
|
20
20
|
export * from "./runtime-env.js";
|
|
21
|
+
export * from "./runtime-skills.js";
|
|
21
22
|
export * from "./mcp/report-progress.js";
|
|
22
23
|
export * from "./runtimes/index.js";
|
|
23
24
|
export * from "./runtimes/engine.js";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
export function runCourseSkillsRelay(argv = process.argv.slice(2)) {
|
|
7
|
+
const socketPath = relaySocketPath(argv);
|
|
8
|
+
const socket = net.createConnection({ path: socketPath });
|
|
9
|
+
const fail = () => {
|
|
10
|
+
process.stderr.write("course_skills relay unavailable\n");
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
};
|
|
13
|
+
socket.once("error", fail);
|
|
14
|
+
socket.once("connect", () => {
|
|
15
|
+
process.stdin.pipe(socket);
|
|
16
|
+
socket.pipe(process.stdout);
|
|
17
|
+
});
|
|
18
|
+
socket.once("close", () => {
|
|
19
|
+
if (!process.stdin.readableEnded)
|
|
20
|
+
process.stdin.destroy();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function relaySocketPath(argv) {
|
|
24
|
+
if (argv.length !== 2 || argv[0] !== "--socket") {
|
|
25
|
+
throw new Error("course_skills relay requires --socket");
|
|
26
|
+
}
|
|
27
|
+
const value = argv[1] ?? "";
|
|
28
|
+
if (!path.isAbsolute(value)
|
|
29
|
+
|| value.includes("\0")
|
|
30
|
+
|| Buffer.byteLength(value, "utf8") > 240) {
|
|
31
|
+
throw new Error("course_skills relay socket is invalid");
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function isMainModule() {
|
|
36
|
+
const entry = process.argv[1];
|
|
37
|
+
if (!entry)
|
|
38
|
+
return false;
|
|
39
|
+
try {
|
|
40
|
+
return realpathSync(entry) === fileURLToPath(import.meta.url);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (isMainModule()) {
|
|
47
|
+
try {
|
|
48
|
+
runCourseSkillsRelay();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
process.stderr.write("course_skills relay configuration invalid\n");
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type PreparedRuntimeSkillProvider, type RuntimeSkillCatalogEntry, type RuntimeSkillEvent } from "../runtime-skills.js";
|
|
2
|
+
type JsonRpcId = string | number | null;
|
|
3
|
+
interface JsonRpcRequest {
|
|
4
|
+
jsonrpc?: unknown;
|
|
5
|
+
id?: unknown;
|
|
6
|
+
method?: unknown;
|
|
7
|
+
params?: unknown;
|
|
8
|
+
}
|
|
9
|
+
interface JsonRpcResponse {
|
|
10
|
+
jsonrpc: "2.0";
|
|
11
|
+
id: JsonRpcId;
|
|
12
|
+
result?: unknown;
|
|
13
|
+
error?: {
|
|
14
|
+
code: number;
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface CourseSkillsMcpServer {
|
|
19
|
+
socketPath: string;
|
|
20
|
+
catalog: RuntimeSkillCatalogEntry[];
|
|
21
|
+
markApplied(): Promise<void>;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export interface CourseSkillsMcpServerOptions {
|
|
25
|
+
prepared: PreparedRuntimeSkillProvider;
|
|
26
|
+
onEvent(event: RuntimeSkillEvent): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Managed Agent Service root. `undefined` auto-detects it; `null` is only for
|
|
29
|
+
* private same-UID tests/BYOA experiments.
|
|
30
|
+
*/
|
|
31
|
+
managedRoot?: string | null;
|
|
32
|
+
}
|
|
33
|
+
export declare class CourseSkillsMcpRuntime {
|
|
34
|
+
private readonly options;
|
|
35
|
+
private readonly grantsByRef;
|
|
36
|
+
private readonly loadedSkills;
|
|
37
|
+
private readonly loadedReferences;
|
|
38
|
+
private referenceBytes;
|
|
39
|
+
constructor(options: CourseSkillsMcpServerOptions);
|
|
40
|
+
emitAppliedEvents(): Promise<void>;
|
|
41
|
+
handle(request: JsonRpcRequest): Promise<JsonRpcResponse | null>;
|
|
42
|
+
private loadSkill;
|
|
43
|
+
private loadReference;
|
|
44
|
+
private requireGrant;
|
|
45
|
+
private emit;
|
|
46
|
+
private emitLoadFailed;
|
|
47
|
+
}
|
|
48
|
+
export declare function startCourseSkillsMcpServer(options: CourseSkillsMcpServerOptions): Promise<CourseSkillsMcpServer>;
|
|
49
|
+
export {};
|