@nowcrew/daemon 0.6.79-beta.1 → 0.6.79
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/agent-ability/resolver.js +10 -7
- package/dist/daemon-update-controller.js +4 -1
- package/dist/daemon-update-eligibility.js +1 -1
- package/dist/execution-protocol.js +1 -0
- package/dist/execution-runner.js +9 -3
- package/dist/local-execution-prompt.js +3 -2
- package/dist/local-executor.js +50 -37
- package/dist/machine-info.js +3 -0
- package/dist/project-skills/capability.js +109 -0
- package/dist/project-skills/controller-convergence.js +57 -0
- package/dist/project-skills/projection-set-switch.js +419 -0
- package/dist/project-skills/projection-state-domain.js +170 -0
- package/dist/project-skills/projection-state-store.js +842 -0
- package/dist/project-skills/projection-state-transaction.js +322 -0
- package/dist/project-skills/projection-state.js +3 -0
- package/dist/project-skills/runtime-root-bootstrap.js +53 -0
- package/dist/project-skills/runtime-root-domain.js +268 -0
- package/dist/project-skills/runtime-root-gc.js +293 -0
- package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
- package/dist/project-skills/runtime-root-leases.js +487 -0
- package/dist/project-skills/runtime-root-source-identity.js +60 -0
- package/dist/project-skills/runtime-root-startup.js +50 -0
- package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
- package/dist/project-skills/runtime-root-state-index.js +356 -0
- package/dist/project-skills/runtime-root-store.js +722 -0
- package/dist/project-skills/serve-capability.js +28 -0
- package/dist/project-skills/serve-startup.js +60 -0
- package/dist/provider-env.js +21 -3
- package/dist/runner.js +2 -1
- package/dist/runtimes/codex-app-server-runner.js +9 -1
- package/dist/runtimes/codex-deepseek-config.js +17 -7
- package/dist/runtimes/codex.js +3 -0
- package/dist/self-training-workspace.js +15 -0
- package/dist/supervised-runtime.js +1 -0
- package/dist/workspace.js +13 -10
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { WebSocket } from "ws";
|
|
2
|
+
import { daemonCapabilityBindings, } from "../machine-info.js";
|
|
3
|
+
import { prepareProjectSkillConnectionCapabilities, } from "./capability.js";
|
|
4
|
+
export function createProjectSkillConnectionCapabilityProvider(input) {
|
|
5
|
+
return async () => daemonCapabilityBindings(input.platform, await prepareProjectSkillConnectionCapabilities(input));
|
|
6
|
+
}
|
|
7
|
+
export function createProjectSkillMachineHelloPublisher(input) {
|
|
8
|
+
let latest = null;
|
|
9
|
+
let latestSocket = null;
|
|
10
|
+
const republish = () => {
|
|
11
|
+
if (latest === null || latestSocket?.readyState !== WebSocket.OPEN)
|
|
12
|
+
return;
|
|
13
|
+
const hello = input.effective(latest, input.status());
|
|
14
|
+
try {
|
|
15
|
+
latestSocket.send(JSON.stringify(hello));
|
|
16
|
+
input.onSent(hello);
|
|
17
|
+
}
|
|
18
|
+
catch { /* 非 OPEN,忽略 */ }
|
|
19
|
+
};
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
publish: (hello, socket) => {
|
|
22
|
+
latest = hello;
|
|
23
|
+
latestSocket = socket;
|
|
24
|
+
republish();
|
|
25
|
+
},
|
|
26
|
+
republish,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createExecutionJournal } from "../execution-journal.js";
|
|
2
|
+
import { reconcileExecutionJournal } from "../execution-recovery.js";
|
|
3
|
+
import { dslog, flushSlog } from "../slog.js";
|
|
4
|
+
/**
|
|
5
|
+
* 每次连接 preflight 的 Runtime root / lease 恢复。重连时本进程仍在跑的 execution 合法持有 lease,
|
|
6
|
+
* 必须当作存活而非孤儿;失败也必须留痕,否则整条连接静默摘掉 Project Skills 能力
|
|
7
|
+
* (incident: 2026-09-02,绑定项目的 Agent 全部 execution_spec_incompatible)。
|
|
8
|
+
*/
|
|
9
|
+
export function createProjectSkillReconnectRecovery(startup, agentsRoot, journal, protectedExecutionIds, liveExecutionIds) {
|
|
10
|
+
return async () => {
|
|
11
|
+
const live = liveExecutionIds();
|
|
12
|
+
try {
|
|
13
|
+
await startup.recover(agentsRoot, journal(), protectedExecutionIds, live);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
dslog("project_skills.preflight_recovery_failed", "Project Skill runtime root 恢复失败,本连接不宣告 Project Skills 能力", {
|
|
18
|
+
level: "ERROR",
|
|
19
|
+
error_code: error.code ?? "unknown",
|
|
20
|
+
error_message: error.message,
|
|
21
|
+
live_execution_count: live.size,
|
|
22
|
+
});
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export async function initializeProjectSkillProtectedExecutionJournal(input) {
|
|
28
|
+
return input.startup.initializeAfterLeaseProtection(input.config.agentsRoot, async (snapshot) => {
|
|
29
|
+
input.protectedExecutionIds.clear();
|
|
30
|
+
for (const executionId of snapshot)
|
|
31
|
+
input.protectedExecutionIds.add(executionId);
|
|
32
|
+
const journal = input.injectedJournal ?? createExecutionJournal(input.config.agentsRoot, {
|
|
33
|
+
protectedExecutionIds: () => input.protectedExecutionIds,
|
|
34
|
+
onOperationTiming: (event) => {
|
|
35
|
+
if (event.operation !== "mark_runtime_ready"
|
|
36
|
+
&& event.queueWaitMs < 250
|
|
37
|
+
&& event.operationMs < 250)
|
|
38
|
+
return;
|
|
39
|
+
dslog("execution.journal_operation", "execution journal 操作完成", {
|
|
40
|
+
level: event.outcome === "succeeded" ? "INFO" : "ERROR",
|
|
41
|
+
execution_id: event.executionId,
|
|
42
|
+
operation: event.operation,
|
|
43
|
+
outcome: event.outcome,
|
|
44
|
+
queue_wait_ms: event.queueWaitMs,
|
|
45
|
+
operation_ms: event.operationMs,
|
|
46
|
+
total_ms: event.queueWaitMs + event.operationMs,
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
await reconcileExecutionJournal(journal, {
|
|
51
|
+
agentsRoot: input.config.agentsRoot,
|
|
52
|
+
serverUrl: input.config.serverUrl,
|
|
53
|
+
...(input.profileName === undefined ? {} : { profileName: input.profileName }),
|
|
54
|
+
log: dslog,
|
|
55
|
+
flush: flushSlog,
|
|
56
|
+
writeStderr: (line) => process.stderr.write(line),
|
|
57
|
+
});
|
|
58
|
+
return journal;
|
|
59
|
+
});
|
|
60
|
+
}
|
package/dist/provider-env.js
CHANGED
|
@@ -11,6 +11,24 @@
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { DEEPSEEK_CODEX_KEY_ENV, deepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
13
13
|
import { DEEPSEEK_HARNESS_CONFIG_ENV } from "./runtimes/deepseek-harness.js";
|
|
14
|
+
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15
|
+
const RESERVED_ENV = new Set([
|
|
16
|
+
"PATH",
|
|
17
|
+
"XDG_CONFIG_HOME",
|
|
18
|
+
"XDG_DATA_HOME",
|
|
19
|
+
"XDG_CACHE_HOME",
|
|
20
|
+
"GH_CONFIG_DIR",
|
|
21
|
+
"CLOUDSDK_CONFIG",
|
|
22
|
+
]);
|
|
23
|
+
/** Keep user-provided variables away from daemon-owned runtime and credential fields. */
|
|
24
|
+
export function sanitizeEnvVars(raw) {
|
|
25
|
+
if (!raw)
|
|
26
|
+
return {};
|
|
27
|
+
return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
|
|
28
|
+
&& ENV_KEY_RE.test(key)
|
|
29
|
+
&& !key.toUpperCase().startsWith("CREW_")
|
|
30
|
+
&& !RESERVED_ENV.has(key.toUpperCase())));
|
|
31
|
+
}
|
|
14
32
|
/** custom 模式下必须从继承 env 中剔除的全局路由/凭证变量(避免与注入值叠加或抢优先级)。 */
|
|
15
33
|
const CONFLICTING_ENV = [
|
|
16
34
|
"ANTHROPIC_BASE_URL",
|
|
@@ -39,12 +57,12 @@ const CONFLICTING_CODEX_ENV = [
|
|
|
39
57
|
DEEPSEEK_CODEX_KEY_ENV,
|
|
40
58
|
];
|
|
41
59
|
/** 返回叠加了 provider 隔离的新 env(不改入参);不支持的 runtime/provider 组合原样返回。 */
|
|
42
|
-
export function applyProviderEnv(base, runtime, cfg, homeDir) {
|
|
60
|
+
export function applyProviderEnv(base, runtime, cfg, homeDir, deepSeekHomeOverride) {
|
|
43
61
|
if (runtime === "codex" && cfg.provider === "deepseek") {
|
|
44
62
|
const env = { ...base };
|
|
45
63
|
for (const key of CONFLICTING_CODEX_ENV)
|
|
46
64
|
delete env[key];
|
|
47
|
-
env.CODEX_HOME = deepSeekCodexHome(homeDir);
|
|
65
|
+
env.CODEX_HOME = deepSeekHomeOverride ?? deepSeekCodexHome(homeDir);
|
|
48
66
|
if (cfg.providerApiKey)
|
|
49
67
|
env[DEEPSEEK_CODEX_KEY_ENV] = cfg.providerApiKey;
|
|
50
68
|
return env;
|
|
@@ -100,7 +118,7 @@ export function applyDeepSeekHarnessMachineEnv(base, machineEnv) {
|
|
|
100
118
|
*/
|
|
101
119
|
export function providerFingerprint(runtime, cfg) {
|
|
102
120
|
if (runtime === "codex" && cfg.provider === "deepseek") {
|
|
103
|
-
return `deepseek|${cfg.providerApiKey ? "key" : "nokey"}`;
|
|
121
|
+
return `deepseek|${cfg.providerBaseUrl ?? "default"}|${cfg.providerApiKey ? "key" : "nokey"}`;
|
|
104
122
|
}
|
|
105
123
|
if (runtime !== "claude" || cfg.provider !== "custom")
|
|
106
124
|
return null;
|
package/dist/runner.js
CHANGED
|
@@ -11,7 +11,8 @@ import { readOriginDecisionFile, resetOriginDecisionFile } from "./origin-decisi
|
|
|
11
11
|
import { readBoundImDecisionFile, resetBoundImDecisionFile } from "./bound-im-decision.js";
|
|
12
12
|
import { launchSupervisedRuntime } from "./supervised-runtime.js";
|
|
13
13
|
import { awaitWithCancellation, } from "./runtime-cancellation.js";
|
|
14
|
-
export { awaitExit, exitActivity
|
|
14
|
+
export { awaitExit, exitActivity } from "./local-executor.js";
|
|
15
|
+
export { sanitizeEnvVars } from "./provider-env.js";
|
|
15
16
|
const ICON = {
|
|
16
17
|
init: "🟢", text: "💬", reading: "📖", sending: "📨", checking: "🔎",
|
|
17
18
|
claiming: "📌", crew: "⚙️", tool: "🛠️", tool_result: "↩️", done: "✅", error: "❌",
|
|
@@ -21,9 +21,14 @@ const RunnerInputSchema = z.object({
|
|
|
21
21
|
cwd: z.string().min(1).optional(),
|
|
22
22
|
workspaceRoots: z.array(z.string().min(1)).optional(),
|
|
23
23
|
skillRoots: z.array(z.string().min(1)).optional(),
|
|
24
|
+
networkAccess: z.boolean().optional(),
|
|
24
25
|
codexCollaborationTools: z.boolean().optional(),
|
|
25
26
|
resume: z.boolean(),
|
|
26
|
-
}).strict()
|
|
27
|
+
}).strict().superRefine((input, ctx) => {
|
|
28
|
+
if (input.networkAccess === true && input.effectivePermission !== "workspace_write") {
|
|
29
|
+
ctx.addIssue({ code: "custom", message: "network_access_requires_workspace_write" });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
27
32
|
const RPC_TIMEOUT_MS = 30_000;
|
|
28
33
|
const INITIALIZE_RPC_TIMEOUT_MS = 60_000;
|
|
29
34
|
const ERROR_MESSAGE_CAP = 2_000;
|
|
@@ -613,6 +618,9 @@ async function runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs
|
|
|
613
618
|
...(input.reasoning === undefined || input.reasoning === "default"
|
|
614
619
|
? {}
|
|
615
620
|
: { effort: input.reasoning }),
|
|
621
|
+
...(input.networkAccess === true ? {
|
|
622
|
+
sandboxPolicy: { type: "workspaceWrite", networkAccess: true },
|
|
623
|
+
} : {}),
|
|
616
624
|
});
|
|
617
625
|
logStage(activeStage, "ok", attempt, stageStartedAt);
|
|
618
626
|
turnId = started.turn.id;
|
|
@@ -7,10 +7,20 @@ export const DEEPSEEK_CODEX_BASE_URL = "https://api.deepseek.com/";
|
|
|
7
7
|
export const DEEPSEEK_CODEX_KEY_ENV = "NOWCREW_DEEPSEEK_API_KEY";
|
|
8
8
|
export const DEEPSEEK_CODEX_REASONING_LEVELS = ["low", "high", "max"];
|
|
9
9
|
const PRIVATE_CODEX_HOME = ".codex-deepseek";
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const SELF_TRAINING_PRIVATE_CODEX_HOME = ".codex-deepseek-self-training";
|
|
11
|
+
export function deepSeekCodexHome(homeDir, selfTraining = false) {
|
|
12
|
+
return resolve(homeDir, selfTraining ? SELF_TRAINING_PRIVATE_CODEX_HOME : PRIVATE_CODEX_HOME);
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
+
function normalizedBaseUrl(value) {
|
|
15
|
+
const url = new URL(value);
|
|
16
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
17
|
+
throw new Error("DeepSeek provider base URL must use HTTP(S)");
|
|
18
|
+
}
|
|
19
|
+
if (!url.pathname.endsWith("/"))
|
|
20
|
+
url.pathname = `${url.pathname}/`;
|
|
21
|
+
return url.toString();
|
|
22
|
+
}
|
|
23
|
+
export function renderDeepSeekConfig(codexHome, baseUrl = DEEPSEEK_CODEX_BASE_URL) {
|
|
14
24
|
const catalogPath = join(codexHome, "models.json");
|
|
15
25
|
return [
|
|
16
26
|
`model = ${JSON.stringify(DEEPSEEK_CODEX_MODEL)}`,
|
|
@@ -20,7 +30,7 @@ export function renderDeepSeekConfig(codexHome) {
|
|
|
20
30
|
"",
|
|
21
31
|
"[model_providers.deepseek]",
|
|
22
32
|
'name = "deepseek"',
|
|
23
|
-
`base_url = ${JSON.stringify(
|
|
33
|
+
`base_url = ${JSON.stringify(normalizedBaseUrl(baseUrl))}`,
|
|
24
34
|
'wire_api = "responses"',
|
|
25
35
|
`env_key = ${JSON.stringify(DEEPSEEK_CODEX_KEY_ENV)}`,
|
|
26
36
|
"",
|
|
@@ -40,11 +50,11 @@ async function writePrivateAtomic(path, content) {
|
|
|
40
50
|
await rm(temporary, { force: true });
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
|
-
export async function materializeDeepSeekCodexHome(homeDir) {
|
|
44
|
-
const codexHome = deepSeekCodexHome(homeDir);
|
|
53
|
+
export async function materializeDeepSeekCodexHome(homeDir, options = {}) {
|
|
54
|
+
const codexHome = deepSeekCodexHome(homeDir, options.selfTraining === true);
|
|
45
55
|
await mkdir(codexHome, { recursive: true, mode: 0o700 });
|
|
46
56
|
await chmod(codexHome, 0o700);
|
|
47
57
|
await writePrivateAtomic(join(codexHome, "models.json"), DEEPSEEK_CODEX_CATALOG);
|
|
48
|
-
await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome));
|
|
58
|
+
await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome, options.baseUrl));
|
|
49
59
|
return codexHome;
|
|
50
60
|
}
|
package/dist/runtimes/codex.js
CHANGED
|
@@ -36,6 +36,9 @@ export function buildCodexArgs(input) {
|
|
|
36
36
|
else if (input.effectivePermission === "full_access" || (input.effectivePermission === undefined && input.dangerous)) {
|
|
37
37
|
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
38
38
|
}
|
|
39
|
+
if (input.effectivePermission === "workspace_write" && input.networkAccess === true) {
|
|
40
|
+
args.push("-c", "sandbox_workspace_write.network_access=true");
|
|
41
|
+
}
|
|
39
42
|
for (const imagePath of input.imagePaths ?? [])
|
|
40
43
|
args.push("--image", imagePath);
|
|
41
44
|
// `-` instructs codex exec to read the prompt from stdin. Keeping the complete prompt out of argv
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, relative, sep } from "node:path";
|
|
3
|
+
/** Delete only a concrete child of this Agent's tasks root; never follow an ambiguous target. */
|
|
4
|
+
export async function cleanupSelfTrainingRunDir(tasksRoot, runDir) {
|
|
5
|
+
const child = relative(tasksRoot, runDir);
|
|
6
|
+
if (child === "" || isAbsolute(child) || child === ".." || child.startsWith(`..${sep}`))
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
await rm(runDir, { recursive: true, force: true });
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
13
|
+
process.stderr.write(`[execution] failed to remove self-training run dir ${runDir}: ${detail}\n`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -46,6 +46,7 @@ export function supervisorLaunch(request) {
|
|
|
46
46
|
effectivePermission: request.effectivePermission,
|
|
47
47
|
...(request.model === undefined ? {} : { model: request.model }),
|
|
48
48
|
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
49
|
+
...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
|
|
49
50
|
...(request.sessionId === undefined ? {} : { sessionId: request.sessionId }),
|
|
50
51
|
...(request.imagePaths === undefined ? {} : { imagePaths: request.imagePaths }),
|
|
51
52
|
...(request.agentRoot === undefined ? {} : {
|
package/dist/workspace.js
CHANGED
|
@@ -59,12 +59,13 @@ export async function prepareWorkspace(input) {
|
|
|
59
59
|
const crewDir = join(dir, ".crew");
|
|
60
60
|
await mkdir(crewDir, { recursive: true });
|
|
61
61
|
// 预先创建明细目录,让收尾流程可以按功能创建 notes/<topic>.md。
|
|
62
|
-
|
|
62
|
+
if (!input.skipLocalMemory)
|
|
63
|
+
await mkdir(join(dir, "notes"), { recursive: true });
|
|
63
64
|
// MEMORY.md:首次创建"持久身份 + 知识索引"骨架,之后由 agent 自己维护
|
|
64
65
|
const memoryPath = join(dir, "MEMORY.md");
|
|
65
|
-
const memorySeedCreated = !(await exists(memoryPath));
|
|
66
|
+
const memorySeedCreated = !input.skipLocalMemory && !(await exists(memoryPath));
|
|
66
67
|
const localMemoryV2Initialized = await hasLocalMemoryV2Marker(dir);
|
|
67
|
-
if (memorySeedCreated) {
|
|
68
|
+
if (!input.skipLocalMemory && memorySeedCreated) {
|
|
68
69
|
try {
|
|
69
70
|
await writeFile(memoryPath, memorySeed(input.handle, input.description), "utf8");
|
|
70
71
|
}
|
|
@@ -73,13 +74,15 @@ export async function prepareWorkspace(input) {
|
|
|
73
74
|
throw error;
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
|
-
let memory;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
let memory = "";
|
|
78
|
+
if (!input.skipLocalMemory) {
|
|
79
|
+
try {
|
|
80
|
+
memory = await readFile(memoryPath, "utf8");
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logMemoryPrepareFailure(input, "memory_read", error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
// 系统提示词按 execution 隔离;省略 identity 的兼容调用每次使用随机路径。
|
|
85
88
|
const promptDir = join(crewDir, "prompts");
|