@ftai/dsh-plugin-acn 0.0.0-stage → 0.1.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/CHANGELOG.md +79 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +19 -0
- package/README.md +55 -2
- package/README_EN.md +56 -0
- package/cordis.patch.yml +29 -0
- package/docs/acn-compat.md +33 -0
- package/docs/architecture.md +129 -0
- package/docs/assets/activity-view.png +0 -0
- package/docs/assets/install-package.png +0 -0
- package/docs/assets/knowledge-model.png +0 -0
- package/docs/assets/knowledge-view.png +0 -0
- package/docs/assets/personal-configuration.png +0 -0
- package/docs/assets/session-upstream.png +0 -0
- package/docs/assets/upstream-settings.png +0 -0
- package/docs/assets/upstream-team.png +0 -0
- package/docs/command-reference.md +201 -0
- package/docs/config.md +243 -0
- package/docs/development.md +197 -0
- package/docs/dsh-compat.md +59 -0
- package/docs/index.md +16 -0
- package/docs/outstanding-issues.md +61 -0
- package/docs/user_guide.md +183 -0
- package/icon.svg +8 -0
- package/lib/activities-LRoCwpzw.js +327 -0
- package/lib/alignment-CwH0nBf-.js +80 -0
- package/lib/auth-DFI9yowX.js +71 -0
- package/lib/checkpoint-Bmt8q2vB.js +188 -0
- package/lib/checkpoint-RgcdqifN.js +318 -0
- package/lib/claim-DOAGe8ZK.js +168 -0
- package/lib/client.js +18420 -0
- package/lib/deliver-BuYQaPRW.js +168 -0
- package/lib/delivery-status-D5m3178s.js +87 -0
- package/lib/dispute-CtrInHzf.js +20 -0
- package/lib/disputes-ByEmi43j.js +51 -0
- package/lib/dream-agent-DBszTHXY.js +249 -0
- package/lib/dream.js +4 -0
- package/lib/effects-DJ4Xa7Tx.js +268 -0
- package/lib/errors-Bp_2WXBr.js +19 -0
- package/lib/errors-pPumDOGD.js +18 -0
- package/lib/evidence-store-DLFvSuNa.js +1030 -0
- package/lib/formation-D4LO31WW.js +187 -0
- package/lib/formation-replay-CEOy5FkI.js +87 -0
- package/lib/formation.js +9 -0
- package/lib/inbox-2CugZZQm.js +188 -0
- package/lib/inbox-DFtE2fOp.js +71 -0
- package/lib/inbox.js +3 -0
- package/lib/index.js +1377 -0
- package/lib/lock-DbmlKOoX.js +332 -0
- package/lib/pages-BEWC3kmZ.js +51 -0
- package/lib/paths-CDYV5UH9.js +154 -0
- package/lib/plugins/commands.js +590 -0
- package/lib/plugins/dream.js +66 -0
- package/lib/plugins/formation.js +66 -0
- package/lib/plugins/inbox.js +53 -0
- package/lib/plugins/knowledge.js +1111 -0
- package/lib/plugins/prompt.js +102 -0
- package/lib/plugins/tools.js +280 -0
- package/lib/policy-DnyHh7Bl.js +16 -0
- package/lib/prepare-DwHZudbA.js +134 -0
- package/lib/presentation-BlLjgDjP.js +47 -0
- package/lib/protocol.js +8 -0
- package/lib/queue-M917xHPS.js +153 -0
- package/lib/regular-file-BFpqMoub.js +136 -0
- package/lib/render-CJaVcKKC.js +48 -0
- package/lib/reported-sets-DdEKdeVb.js +122 -0
- package/lib/revision-DeoL_IUN.js +11 -0
- package/lib/scheduler-MaUEbXr3.js +64 -0
- package/lib/service-B7AI5jds.js +344 -0
- package/lib/service-BcHV5mc7.js +305 -0
- package/lib/service-CNjqHCBR.js +505 -0
- package/lib/service-C_i3BzdP.js +1106 -0
- package/lib/session-binding-BQrOGhZI.js +151 -0
- package/lib/session-state-CYqUhC95.js +38 -0
- package/lib/sessions-C2xuPFQv.js +218 -0
- package/lib/store.js +16 -0
- package/lib/structured-lyQCK-Zk.js +452 -0
- package/lib/subagent-yJsFxHmH.js +9 -0
- package/lib/team-credentials-C9xHrPIi.js +579 -0
- package/lib/team.js +3 -0
- package/lib/tool-schema-3b4gH1zf.js +28 -0
- package/lib/trace-Da0RnL5R.js +14 -0
- package/lib/traces-CHBo8y1z.js +11 -0
- package/lib/types/activities.d.ts +71 -0
- package/lib/types/advanced-configuration.d.ts +11 -0
- package/lib/types/client/ActivityDetails.d.ts +18 -0
- package/lib/types/client/AdvancedPreferences.d.ts +8 -0
- package/lib/types/client/BlankCommandFeedback.d.ts +10 -0
- package/lib/types/client/ComposerMenu.d.ts +24 -0
- package/lib/types/client/ConfigurationPage.d.ts +11 -0
- package/lib/types/client/ConnectionCheckDialog.d.ts +12 -0
- package/lib/types/client/KnowledgeDetail.d.ts +6 -0
- package/lib/types/client/KnowledgeDirectoryField.d.ts +11 -0
- package/lib/types/client/KnowledgePanel.d.ts +12 -0
- package/lib/types/client/KnowledgeReference.d.ts +13 -0
- package/lib/types/client/SessionToolbar.d.ts +16 -0
- package/lib/types/client/SettingsToast.d.ts +3 -0
- package/lib/types/client/SourceSessionLink.d.ts +15 -0
- package/lib/types/client/TeamCredentialField.d.ts +16 -0
- package/lib/types/client/TeamSettingsPage.d.ts +30 -0
- package/lib/types/client/configuration-errors.d.ts +2 -0
- package/lib/types/client/interaction-styles.d.ts +1 -0
- package/lib/types/client/interaction-ui.d.ts +30 -0
- package/lib/types/client/knowledge-labels.d.ts +5 -0
- package/lib/types/client/locales.d.ts +51 -0
- package/lib/types/commands/claim.d.ts +4 -0
- package/lib/types/commands/import.d.ts +10 -0
- package/lib/types/commands/status.d.ts +5 -0
- package/lib/types/config.d.ts +122 -0
- package/lib/types/configuration.d.ts +259 -0
- package/lib/types/credentials.d.ts +65 -0
- package/lib/types/dream/audit.d.ts +4 -0
- package/lib/types/dream/autonomous.d.ts +185 -0
- package/lib/types/dream/candidates.d.ts +34 -0
- package/lib/types/dream/checkpoint.d.ts +35 -0
- package/lib/types/dream/evidence-files.d.ts +10 -0
- package/lib/types/dream/evidence-store.d.ts +103 -0
- package/lib/types/dream/evidence.d.ts +30 -0
- package/lib/types/dream/index.d.ts +10 -0
- package/lib/types/dream/plan.d.ts +18 -0
- package/lib/types/dream/preservation.d.ts +6 -0
- package/lib/types/dream/review.d.ts +10 -0
- package/lib/types/dream/schema.d.ts +1475 -0
- package/lib/types/dream/service.d.ts +56 -0
- package/lib/types/dream/sync.d.ts +147 -0
- package/lib/types/dsh-adapter/configuration-remote.d.ts +1 -0
- package/lib/types/dsh-adapter/configuration-service.d.ts +28 -0
- package/lib/types/dsh-adapter/dream-agent.d.ts +4 -0
- package/lib/types/dsh-adapter/dream-evidence.d.ts +3 -0
- package/lib/types/dsh-adapter/formation-archive.d.ts +3 -0
- package/lib/types/dsh-adapter/formation-replay.d.ts +5 -0
- package/lib/types/dsh-adapter/formation.d.ts +4 -0
- package/lib/types/dsh-adapter/interaction-service.d.ts +13 -0
- package/lib/types/dsh-adapter/knowledge-paths.d.ts +3 -0
- package/lib/types/dsh-adapter/llm.d.ts +10 -0
- package/lib/types/dsh-adapter/project-upstream.d.ts +4 -0
- package/lib/types/dsh-adapter/runtime-pool.d.ts +110 -0
- package/lib/types/dsh-adapter/session-binding.d.ts +43 -0
- package/lib/types/dsh-adapter/source-sessions.d.ts +3 -0
- package/lib/types/dsh-adapter/team-credentials.d.ts +5 -0
- package/lib/types/dsh-adapter/tool-schema.d.ts +4 -0
- package/lib/types/dsh-adapter/tools.d.ts +5 -0
- package/lib/types/formation/capture.d.ts +19 -0
- package/lib/types/formation/checkpoint.d.ts +136 -0
- package/lib/types/formation/index.d.ts +10 -0
- package/lib/types/formation/prepare.d.ts +16 -0
- package/lib/types/formation/schema.d.ts +45 -0
- package/lib/types/formation/service.d.ts +58 -0
- package/lib/types/formation/transcript.d.ts +75 -0
- package/lib/types/formation/triggers.d.ts +3 -0
- package/lib/types/inbox/cascade.d.ts +3 -0
- package/lib/types/inbox/effects.d.ts +275 -0
- package/lib/types/inbox/index.d.ts +5 -0
- package/lib/types/inbox/prepare.d.ts +380 -0
- package/lib/types/inbox/service.d.ts +26 -0
- package/lib/types/inbox/sync.d.ts +9 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/interaction-messages.d.ts +69 -0
- package/lib/types/interaction.d.ts +550 -0
- package/lib/types/knowledge/alignment.d.ts +49 -0
- package/lib/types/knowledge/catalog.d.ts +5 -0
- package/lib/types/knowledge/changes.d.ts +32 -0
- package/lib/types/knowledge/disputes.d.ts +3 -0
- package/lib/types/knowledge/errors.d.ts +9 -0
- package/lib/types/knowledge/ordering.d.ts +7 -0
- package/lib/types/knowledge/pages.d.ts +7 -0
- package/lib/types/knowledge/safety.d.ts +2 -0
- package/lib/types/knowledge/service.d.ts +52 -0
- package/lib/types/knowledge/sessions.d.ts +59 -0
- package/lib/types/knowledge/sources.d.ts +2 -0
- package/lib/types/knowledge/timestamps.d.ts +1 -0
- package/lib/types/knowledge/types.d.ts +84 -0
- package/lib/types/llm/response.d.ts +1 -0
- package/lib/types/llm/structured.d.ts +27 -0
- package/lib/types/local-name.d.ts +1 -0
- package/lib/types/plugins/client.d.ts +9 -0
- package/lib/types/plugins/commands.d.ts +11 -0
- package/lib/types/plugins/configuration.d.ts +8 -0
- package/lib/types/plugins/dream.d.ts +16 -0
- package/lib/types/plugins/formation.d.ts +16 -0
- package/lib/types/plugins/inbox.d.ts +16 -0
- package/lib/types/plugins/knowledge.d.ts +19 -0
- package/lib/types/plugins/prompt.d.ts +17 -0
- package/lib/types/plugins/tools.d.ts +10 -0
- package/lib/types/prompt/render.d.ts +2 -0
- package/lib/types/protocol/arbitration.d.ts +154 -0
- package/lib/types/protocol/auth.d.ts +31 -0
- package/lib/types/protocol/claim.d.ts +36 -0
- package/lib/types/protocol/dispute.d.ts +17 -0
- package/lib/types/protocol/ids.d.ts +20 -0
- package/lib/types/protocol/inbox.d.ts +141 -0
- package/lib/types/protocol/index.d.ts +11 -0
- package/lib/types/protocol/policy.d.ts +21 -0
- package/lib/types/protocol/revision.d.ts +2 -0
- package/lib/types/protocol/router.d.ts +114 -0
- package/lib/types/protocol/schema.d.ts +4 -0
- package/lib/types/protocol/time.d.ts +5 -0
- package/lib/types/protocol/trace.d.ts +11 -0
- package/lib/types/store/atomic.d.ts +8 -0
- package/lib/types/store/claim-edits.d.ts +33 -0
- package/lib/types/store/claims.d.ts +7 -0
- package/lib/types/store/context.d.ts +13 -0
- package/lib/types/store/delivery-status.d.ts +47 -0
- package/lib/types/store/disputes.d.ts +6 -0
- package/lib/types/store/entities.d.ts +16 -0
- package/lib/types/store/errors.d.ts +8 -0
- package/lib/types/store/inbox.d.ts +27 -0
- package/lib/types/store/index.d.ts +16 -0
- package/lib/types/store/jobs.d.ts +5 -0
- package/lib/types/store/lock.d.ts +11 -0
- package/lib/types/store/path-names.d.ts +2 -0
- package/lib/types/store/paths.d.ts +45 -0
- package/lib/types/store/presentation.d.ts +13 -0
- package/lib/types/store/regular-file.d.ts +2 -0
- package/lib/types/store/rejected-disputes.d.ts +18 -0
- package/lib/types/store/rename.d.ts +1 -0
- package/lib/types/store/reported-sets.d.ts +24 -0
- package/lib/types/store/session-state.d.ts +17 -0
- package/lib/types/store/traces.d.ts +6 -0
- package/lib/types/store/uploads.d.ts +312 -0
- package/lib/types/store/yaml.d.ts +8 -0
- package/lib/types/team/errors.d.ts +10 -0
- package/lib/types/team/http.d.ts +20 -0
- package/lib/types/team/index.d.ts +5 -0
- package/lib/types/team/maintainer-client.d.ts +18 -0
- package/lib/types/team/router-client.d.ts +14 -0
- package/lib/types/team/service.d.ts +28 -0
- package/lib/types/team/system-proxy.d.ts +9 -0
- package/lib/types/tools/claim.d.ts +63 -0
- package/lib/types/tools/consult-router.d.ts +11 -0
- package/lib/types/tools/subagent.d.ts +8 -0
- package/lib/types/tools/validation.d.ts +2 -0
- package/lib/types/upload/consolidation.d.ts +63 -0
- package/lib/types/upload/deliver.d.ts +15 -0
- package/lib/types/upload/index.d.ts +2 -0
- package/lib/types/upload/scheduler.d.ts +19 -0
- package/lib/types/upstreams.d.ts +5 -0
- package/lib/types/worker/queue.d.ts +17 -0
- package/lib/types/worker/types.d.ts +51 -0
- package/lib/types/worker/worker.d.ts +39 -0
- package/lib/types-C2ST2Zdg.js +17 -0
- package/lib/upload.js +3 -0
- package/lib/uploads-BCA2p92b.js +210 -0
- package/lib/worker-BBTYP2RE.js +185 -0
- package/locale/commands/en.json +1 -0
- package/locale/commands/zh.json +1 -0
- package/locale/dream/en.json +1 -0
- package/locale/dream/zh.json +1 -0
- package/locale/en.json +7 -0
- package/locale/formation/en.json +1 -0
- package/locale/formation/zh.json +1 -0
- package/locale/inbox/en.json +1 -0
- package/locale/inbox/zh.json +1 -0
- package/locale/knowledge/en.json +1 -0
- package/locale/knowledge/zh.json +1 -0
- package/locale/prompt/en.json +1 -0
- package/locale/prompt/zh.json +1 -0
- package/locale/tools/en.json +1 -0
- package/locale/tools/zh.json +1 -0
- package/locale/zh.json +7 -0
- package/package.json +262 -3
- package/prompts/claim_dream.j2 +77 -0
- package/prompts/claim_dream_legacy.j2 +63 -0
- package/prompts/dream_compaction.j2 +11 -0
- package/prompts/dream_evidence.j2 +5 -0
- package/prompts/inbox_claim_attribute_update_internalize.j2 +112 -0
- package/prompts/inbox_policy_update_internalize.j2 +87 -0
- package/prompts/knowledge.j2 +38 -0
- package/prompts/session_recap.j2 +113 -0
- package/prompts/what_is_claim.j2 +51 -0
- package/prompts/what_is_dispute.j2 +21 -0
- package/prompts/what_is_policy.j2 +22 -0
|
@@ -0,0 +1,1111 @@
|
|
|
1
|
+
import { o as RouteSchema, t as abortable } from "../structured-lyQCK-Zk.js";
|
|
2
|
+
import { _ as JobError, c as readYamlOr, l as writeYamlAtomic, n as withFileLock, w as hasCode } from "../lock-DbmlKOoX.js";
|
|
3
|
+
import { t as AcnDream } from "../service-C_i3BzdP.js";
|
|
4
|
+
import { n as agentHomePath } from "../paths-CDYV5UH9.js";
|
|
5
|
+
import { _ as unboundSession, a as identityOf, c as rememberConfiguration, d as sessionAllowed, f as sessionChanged, g as sessionUnbound, h as sessionSelection, i as forgetConfiguration, l as restoreSessionBinding, m as sessionHasWork, n as configurationActivation, o as markSessionEligible, p as sessionEligible, s as observeSelection, t as bindSession, u as runningConfiguration } from "../session-binding-BQrOGhZI.js";
|
|
6
|
+
import { n as projectEvent, r as structuredBackend, t as compactionEnd } from "../formation-D4LO31WW.js";
|
|
7
|
+
import { t as autonomousDreamRunner } from "../dream-agent-DBszTHXY.js";
|
|
8
|
+
import { t as loadPromptRenderer } from "../render-CJaVcKKC.js";
|
|
9
|
+
import { n as isSubagent, t as isEvidenceAgent } from "../subagent-yJsFxHmH.js";
|
|
10
|
+
import { t as UploadScheduler } from "../scheduler-MaUEbXr3.js";
|
|
11
|
+
import { t as AcnInbox } from "../service-BcHV5mc7.js";
|
|
12
|
+
import { t as AcnFormation } from "../service-CNjqHCBR.js";
|
|
13
|
+
import { n as formationReplay, r as connectArchives, t as formationOwnership } from "../formation-replay-CEOy5FkI.js";
|
|
14
|
+
import { a as defaultUpstream, d as resolveConfig, f as upstreamsOf, i as canonicalProjectPath, n as teamCredentials, o as resolveUpstream, r as AcnKnowledge } from "../team-credentials-C9xHrPIi.js";
|
|
15
|
+
import { t as KnowledgeSessions } from "../sessions-C2xuPFQv.js";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
18
|
+
import fs from "node:fs/promises";
|
|
19
|
+
import { dirname, join, resolve } from "node:path";
|
|
20
|
+
import { appendPluginRecord } from "@deepseek-ai/dsh-session";
|
|
21
|
+
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
22
|
+
import Schema from "@deepseek-ai/schemastery";
|
|
23
|
+
//#region src/dsh-adapter/knowledge-paths.ts
|
|
24
|
+
const fileTools = /* @__PURE__ */ new Set([
|
|
25
|
+
"read",
|
|
26
|
+
"read_image",
|
|
27
|
+
"write",
|
|
28
|
+
"edit",
|
|
29
|
+
"grep",
|
|
30
|
+
"glob",
|
|
31
|
+
"str_replace_editor"
|
|
32
|
+
]);
|
|
33
|
+
const denial = "普通文件工具不能访问私有 Agent 数据或包含它的搜索根;请使用 Claim 专用工具,或缩小搜索目录。";
|
|
34
|
+
const fingerprint = (execution) => createHash("sha256").update(JSON.stringify(execution.arguments) ?? "undefined").digest("hex");
|
|
35
|
+
async function* linkedFiles(root, signal) {
|
|
36
|
+
const directories = [root];
|
|
37
|
+
while (directories.length > 0) {
|
|
38
|
+
signal.throwIfAborted();
|
|
39
|
+
const directory = directories.pop();
|
|
40
|
+
if (directory === void 0) break;
|
|
41
|
+
let entries;
|
|
42
|
+
try {
|
|
43
|
+
entries = await fs.readdir(directory, { withFileTypes: true });
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (hasCode(error, "ENOENT")) continue;
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
signal.throwIfAborted();
|
|
50
|
+
const path = join(directory, entry.name);
|
|
51
|
+
if (entry.isDirectory()) directories.push(path);
|
|
52
|
+
else if (entry.isFile()) try {
|
|
53
|
+
const info = await fs.lstat(path);
|
|
54
|
+
if (info.nlink > 1) yield `${info.dev}:${info.ino}`;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
if (!hasCode(error, "ENOENT")) throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function protectKnowledgePaths(ctx, knowledge) {
|
|
62
|
+
const privateHome = dirname(dirname(agentHomePath(knowledge.config.home, "private-root")));
|
|
63
|
+
const privateAlias = async (path, recursive, signal) => {
|
|
64
|
+
const privateLinks = /* @__PURE__ */ new Set();
|
|
65
|
+
for await (const identity of linkedFiles(privateHome, signal)) privateLinks.add(identity);
|
|
66
|
+
if (privateLinks.size === 0) return false;
|
|
67
|
+
if (recursive) {
|
|
68
|
+
for await (const identity of linkedFiles(path, signal)) if (privateLinks.has(identity)) return true;
|
|
69
|
+
} else {
|
|
70
|
+
const info = await fs.lstat(path);
|
|
71
|
+
return privateLinks.has(`${info.dev}:${info.ino}`);
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
const validated = /* @__PURE__ */ new WeakMap();
|
|
76
|
+
const targetAllowed = async (target, recursive, signal) => {
|
|
77
|
+
const mapped = ctx.fs.processPathFromHostPath(dirname(dirname(agentHomePath(knowledge.config.home, "private-root"))));
|
|
78
|
+
if (mapped === void 0) return true;
|
|
79
|
+
const root = await ctx.fs.resolve(mapped, signal === void 0 ? {} : { signal });
|
|
80
|
+
return !ctx.fs.contains(root, target) && !(recursive && ctx.fs.contains(target, root));
|
|
81
|
+
};
|
|
82
|
+
ctx.on("fs/write-intent", async (target, _actor, next) => {
|
|
83
|
+
if (!await targetAllowed(target, false)) throw new Error(denial);
|
|
84
|
+
return next();
|
|
85
|
+
});
|
|
86
|
+
ctx.on("fs/edit-intent", async (target, _actor, next) => {
|
|
87
|
+
if (!await targetAllowed(target, false)) throw new Error(denial);
|
|
88
|
+
return next();
|
|
89
|
+
});
|
|
90
|
+
ctx.on("tools/pre-execute", async (execution, next) => {
|
|
91
|
+
const decision = await next();
|
|
92
|
+
if (decision.kind === "deny" || decision.kind === "cancel" || !fileTools.has(execution.name)) return decision;
|
|
93
|
+
const args = execution.arguments;
|
|
94
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return {
|
|
95
|
+
kind: "deny",
|
|
96
|
+
reason: denial
|
|
97
|
+
};
|
|
98
|
+
const fields = args;
|
|
99
|
+
const search = execution.name === "grep" || execution.name === "glob";
|
|
100
|
+
const key = "file_path" in fields ? "file_path" : "path" in fields || search ? "path" : void 0;
|
|
101
|
+
if (key === void 0 || fields[key] !== void 0 && typeof fields[key] !== "string") return {
|
|
102
|
+
kind: "deny",
|
|
103
|
+
reason: denial
|
|
104
|
+
};
|
|
105
|
+
const cwd = execution.agent === void 0 ? process.cwd() : await ctx.workingDirectory.ensure(execution.agent, execution.signal);
|
|
106
|
+
const target = await ctx.fs.resolve(typeof fields[key] === "string" ? fields[key] : cwd, {
|
|
107
|
+
cwd,
|
|
108
|
+
signal: execution.signal
|
|
109
|
+
});
|
|
110
|
+
const info = await ctx.fs.stat(target, execution.signal);
|
|
111
|
+
if (!await targetAllowed(target, search || info?.type === "directory", execution.signal)) return {
|
|
112
|
+
kind: "deny",
|
|
113
|
+
reason: denial
|
|
114
|
+
};
|
|
115
|
+
const path = ctx.fs.processPath(target);
|
|
116
|
+
if (ctx.fs.processPathFromHostPath(resolve(path)) === path) try {
|
|
117
|
+
const stat = await fs.lstat(path);
|
|
118
|
+
if ((stat.isFile() && stat.nlink > 1 || search && stat.isDirectory()) && await privateAlias(path, search && stat.isDirectory(), execution.signal)) return {
|
|
119
|
+
kind: "deny",
|
|
120
|
+
reason: denial
|
|
121
|
+
};
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (!hasCode(error, "ENOENT")) throw error;
|
|
124
|
+
}
|
|
125
|
+
validated.set(execution, {
|
|
126
|
+
fingerprint: fingerprint(execution),
|
|
127
|
+
cwd
|
|
128
|
+
});
|
|
129
|
+
return decision;
|
|
130
|
+
});
|
|
131
|
+
ctx.tools.guard((execution) => {
|
|
132
|
+
if (!fileTools.has(execution.name)) return;
|
|
133
|
+
const approved = validated.get(execution);
|
|
134
|
+
const cwd = execution.agent === void 0 ? process.cwd() : ctx.workingDirectory.get(execution.agent.session);
|
|
135
|
+
return approved?.fingerprint !== fingerprint(execution) || approved.cwd !== cwd ? denial : void 0;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/dsh-adapter/project-upstream.ts
|
|
140
|
+
async function recentProjectSelection(ctx, current) {
|
|
141
|
+
if (current.cwd === void 0 || isSubagent(current)) return void 0;
|
|
142
|
+
try {
|
|
143
|
+
const persistence = ctx.get("sessionPersistence");
|
|
144
|
+
const headers = /* @__PURE__ */ new Map();
|
|
145
|
+
if (persistence !== void 0) for (const snapshot of await persistence.list()) headers.set(snapshot.header.id, snapshot.header);
|
|
146
|
+
for (const session of ctx.sessions.list()) headers.set(session.id, session.header);
|
|
147
|
+
const project = await canonicalProjectPath(current.cwd);
|
|
148
|
+
const paths = /* @__PURE__ */ new Map();
|
|
149
|
+
const candidates = [...headers.values()].filter((header) => header.id !== current.id && header.createdAt <= current.createdAt && header.cwd !== void 0 && !isSubagent(header));
|
|
150
|
+
candidates.sort((left, right) => right.createdAt - left.createdAt || right.id.localeCompare(left.id));
|
|
151
|
+
for (const header of candidates) {
|
|
152
|
+
if (header.cwd === void 0) continue;
|
|
153
|
+
let path = paths.get(header.cwd);
|
|
154
|
+
if (path === void 0) {
|
|
155
|
+
path = await canonicalProjectPath(header.cwd);
|
|
156
|
+
paths.set(header.cwd, path);
|
|
157
|
+
}
|
|
158
|
+
if (path !== project) continue;
|
|
159
|
+
const live = ctx.sessions.get(header.id);
|
|
160
|
+
if (live !== void 0) {
|
|
161
|
+
const selection = sessionSelection(ctx, live.header);
|
|
162
|
+
if (selection === void 0) continue;
|
|
163
|
+
return selection;
|
|
164
|
+
}
|
|
165
|
+
if (persistence === void 0) continue;
|
|
166
|
+
const handle = await persistence.open(header.id, "read");
|
|
167
|
+
try {
|
|
168
|
+
const { events } = await handle.read();
|
|
169
|
+
restoreSessionBinding(handle.header, events);
|
|
170
|
+
return sessionSelection(ctx, handle.header);
|
|
171
|
+
} finally {
|
|
172
|
+
await handle.close();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} catch {
|
|
176
|
+
ctx.logger.warn("Could not read the recent project selection; the global default will be used.");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/dsh-adapter/runtime-pool.ts
|
|
181
|
+
var RuntimePool = class {
|
|
182
|
+
owner;
|
|
183
|
+
base;
|
|
184
|
+
runtimes = /* @__PURE__ */ new Map();
|
|
185
|
+
ready = /* @__PURE__ */ new Map();
|
|
186
|
+
workers = /* @__PURE__ */ new Map();
|
|
187
|
+
preferredWorkers = /* @__PURE__ */ new Map();
|
|
188
|
+
retiredWorkers = /* @__PURE__ */ new Set();
|
|
189
|
+
starting = Promise.resolve();
|
|
190
|
+
instructions = /* @__PURE__ */ new Map();
|
|
191
|
+
render;
|
|
192
|
+
syncs = /* @__PURE__ */ new Map();
|
|
193
|
+
syncTasks = /* @__PURE__ */ new Map();
|
|
194
|
+
baselines = /* @__PURE__ */ new Set();
|
|
195
|
+
baselineRenderers = /* @__PURE__ */ new Map();
|
|
196
|
+
admissions = /* @__PURE__ */ new Map();
|
|
197
|
+
resumed = /* @__PURE__ */ new Set();
|
|
198
|
+
attached = /* @__PURE__ */ new Map();
|
|
199
|
+
unboundTools = /* @__PURE__ */ new Map();
|
|
200
|
+
components = /* @__PURE__ */ new Map();
|
|
201
|
+
pending = Promise.resolve();
|
|
202
|
+
closed = false;
|
|
203
|
+
get preparing() {
|
|
204
|
+
return this.runtimes.size !== this.ready.size || [...this.syncs.values()].some((state) => state.phase === "syncing");
|
|
205
|
+
}
|
|
206
|
+
host;
|
|
207
|
+
get ctx() {
|
|
208
|
+
return this.host ?? this.owner;
|
|
209
|
+
}
|
|
210
|
+
constructor(owner, base) {
|
|
211
|
+
this.owner = owner;
|
|
212
|
+
this.base = base;
|
|
213
|
+
owner.inject([
|
|
214
|
+
"agents",
|
|
215
|
+
"sessions",
|
|
216
|
+
"systemPrompt",
|
|
217
|
+
"llm",
|
|
218
|
+
"tools"
|
|
219
|
+
], async (child) => {
|
|
220
|
+
this.host = child;
|
|
221
|
+
await this.install(child);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
component(name) {
|
|
225
|
+
const token = Symbol(name);
|
|
226
|
+
const owners = this.components.get(name) ?? /* @__PURE__ */ new Set();
|
|
227
|
+
owners.add(token);
|
|
228
|
+
this.components.set(name, owners);
|
|
229
|
+
this.schedule(async () => {
|
|
230
|
+
const values = await Promise.allSettled([...this.runtimes.values()]);
|
|
231
|
+
for (const value of values) if (value.status === "fulfilled") await value.value.setComponent(name, true);
|
|
232
|
+
});
|
|
233
|
+
return async () => {
|
|
234
|
+
owners.delete(token);
|
|
235
|
+
if (owners.size > 0) return;
|
|
236
|
+
await this.pending;
|
|
237
|
+
for (const runtime of this.ready.values()) await runtime.setComponent(name, false);
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
supportsJob(key, kind) {
|
|
241
|
+
const config = this.config(key, true);
|
|
242
|
+
if (config === void 0) return false;
|
|
243
|
+
const component = kind === "recap" || kind === "finalize" ? "formation" : kind;
|
|
244
|
+
return this.enabled(component) && (kind !== "dream" || config.dream.enabled);
|
|
245
|
+
}
|
|
246
|
+
enabled(name) {
|
|
247
|
+
return (this.components.get(name)?.size ?? 0) > 0;
|
|
248
|
+
}
|
|
249
|
+
choices() {
|
|
250
|
+
return Object.entries(upstreamsOf(this.base.config)).map(([key, item]) => ({
|
|
251
|
+
key,
|
|
252
|
+
name: item.name || key,
|
|
253
|
+
agentId: item.agentId,
|
|
254
|
+
enabled: item.enabled,
|
|
255
|
+
mode: item.routerEndpoint ? "team" : "solo"
|
|
256
|
+
}));
|
|
257
|
+
}
|
|
258
|
+
config(key, includeDisabled = false) {
|
|
259
|
+
const profiles = upstreamsOf(this.base.config);
|
|
260
|
+
const mapped = profiles[key] === void 0 ? Object.keys(profiles).find((name) => profiles[name]?.legacyKey === key) : key;
|
|
261
|
+
return mapped === void 0 ? void 0 : resolveUpstream(this.base.config, mapped, includeDisabled);
|
|
262
|
+
}
|
|
263
|
+
forSession(header) {
|
|
264
|
+
const config = this.config(this.key(header));
|
|
265
|
+
const running = runningConfiguration(header);
|
|
266
|
+
return config !== void 0 && running !== void 0 && identityOf(running) === identityOf(config) ? running : config;
|
|
267
|
+
}
|
|
268
|
+
runtimeId(key, config) {
|
|
269
|
+
return key + "@" + createHash("sha256").update(JSON.stringify([
|
|
270
|
+
config.home,
|
|
271
|
+
config.agentId,
|
|
272
|
+
config.team,
|
|
273
|
+
config.llm
|
|
274
|
+
])).digest("hex");
|
|
275
|
+
}
|
|
276
|
+
prefer(header) {
|
|
277
|
+
if (isSubagent(header) || !this.allowed(header)) return;
|
|
278
|
+
const config = this.forSession(header);
|
|
279
|
+
if (config !== void 0) this.preferredWorkers.set(identityOf(config), this.runtimeId(this.key(header), config));
|
|
280
|
+
}
|
|
281
|
+
readyFor(header) {
|
|
282
|
+
const config = this.forSession(header);
|
|
283
|
+
return config === void 0 ? void 0 : this.ready.get(this.runtimeId(this.key(header), config));
|
|
284
|
+
}
|
|
285
|
+
backgroundSelection(config) {
|
|
286
|
+
const identity = identityOf(config);
|
|
287
|
+
const key = this.choices().find((choice) => {
|
|
288
|
+
const current = this.config(choice.key);
|
|
289
|
+
return current !== void 0 && identityOf(current) === identity;
|
|
290
|
+
})?.key;
|
|
291
|
+
return key === void 0 ? void 0 : {
|
|
292
|
+
schemaVersion: 1,
|
|
293
|
+
key,
|
|
294
|
+
identity,
|
|
295
|
+
prepared: true
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
selected(header) {
|
|
299
|
+
return sessionSelection(this.ctx, header);
|
|
300
|
+
}
|
|
301
|
+
defaultFor() {
|
|
302
|
+
return defaultUpstream(this.base.config);
|
|
303
|
+
}
|
|
304
|
+
sessionView(header, events) {
|
|
305
|
+
return Promise.resolve(this.view(header, events));
|
|
306
|
+
}
|
|
307
|
+
key(header) {
|
|
308
|
+
const key = this.selected(header)?.key ?? (this.base.config.upstreams === void 0 ? this.base.mode === "team" ? "legacy" : "personal" : this.defaultFor());
|
|
309
|
+
const profiles = upstreamsOf(this.base.config);
|
|
310
|
+
return profiles[key] === void 0 ? Object.keys(profiles).find((name) => profiles[name]?.legacyKey === key) ?? key : key;
|
|
311
|
+
}
|
|
312
|
+
isBase(key) {
|
|
313
|
+
return key === "" || this.base.config.upstreams === void 0 && key === (this.base.mode === "team" ? "legacy" : "personal");
|
|
314
|
+
}
|
|
315
|
+
allowed(header) {
|
|
316
|
+
const selected = this.selected(header);
|
|
317
|
+
if (selected === void 0) return this.base.config.upstreams === void 0 ? sessionAllowed(this.ctx, this.base.config, header) : Object.keys(this.base.config.upstreams).length === 0;
|
|
318
|
+
const config = this.config(selected.key);
|
|
319
|
+
return config !== void 0 && selected.identity === identityOf(config) && config.agentId !== "";
|
|
320
|
+
}
|
|
321
|
+
unbound(header, events) {
|
|
322
|
+
return !this.allowed(header) && sessionUnbound(this.ctx, header, events);
|
|
323
|
+
}
|
|
324
|
+
chatAllowed(header) {
|
|
325
|
+
return this.allowed(header) || this.unbound(header);
|
|
326
|
+
}
|
|
327
|
+
async knowledge(header) {
|
|
328
|
+
await this.pending;
|
|
329
|
+
if (!this.allowed(header)) throw new JobError("identity", this.unbound(header) ? unboundSession : "团队连接不可用或身份已改变,请检查连接,或新建会话。");
|
|
330
|
+
const key = this.key(header);
|
|
331
|
+
return this.isBase(key) ? this.base : (await this.ensure(key, header.cwd, this.forSession(header))).knowledge;
|
|
332
|
+
}
|
|
333
|
+
async service(header) {
|
|
334
|
+
await this.pending;
|
|
335
|
+
if (!this.allowed(header)) throw new JobError("identity", this.unbound(header) ? unboundSession : "当前会话的知识身份不可用。");
|
|
336
|
+
const key = this.key(header);
|
|
337
|
+
if (!this.isBase(key)) {
|
|
338
|
+
const runtime = await this.ensure(key, header.cwd, this.forSession(header));
|
|
339
|
+
return {
|
|
340
|
+
knowledge: runtime.knowledge,
|
|
341
|
+
formation: runtime.components.has("formation") ? runtime.formation : void 0,
|
|
342
|
+
inbox: runtime.components.has("inbox") ? runtime.inbox : void 0,
|
|
343
|
+
dream: runtime.components.has("dream") ? runtime.dream : void 0
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
knowledge: this.base,
|
|
348
|
+
formation: this.ctx.get("acnFormation"),
|
|
349
|
+
inbox: this.ctx.get("acnInbox"),
|
|
350
|
+
dream: this.ctx.get("acnDream")
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
async activate(key) {
|
|
354
|
+
await this.pending;
|
|
355
|
+
const config = this.config(key);
|
|
356
|
+
if (config === void 0 || config.agentId === "") throw new JobError("connection", "知识身份不可用,请恢复连接后重试。");
|
|
357
|
+
this.preferredWorkers.set(identityOf(config), this.runtimeId(key, config));
|
|
358
|
+
return this.isBase(key) ? this.base : (await this.ensure(key)).knowledge;
|
|
359
|
+
}
|
|
360
|
+
async dreamService(key) {
|
|
361
|
+
await this.activate(key);
|
|
362
|
+
if (this.isBase(key)) return this.ctx.get("acnDream");
|
|
363
|
+
const runtime = await this.ensure(key);
|
|
364
|
+
return runtime.components.has("dream") ? runtime.dream : void 0;
|
|
365
|
+
}
|
|
366
|
+
registerBaseline(refresh, render) {
|
|
367
|
+
this.baselines.add(refresh);
|
|
368
|
+
this.baselineRenderers.set(refresh, render);
|
|
369
|
+
return () => {
|
|
370
|
+
this.baselines.delete(refresh);
|
|
371
|
+
this.baselineRenderers.delete(refresh);
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
hasUser(header) {
|
|
375
|
+
const session = this.ctx.sessions.get(header.id);
|
|
376
|
+
return session === void 0 || session.snapshotEvents().some((event) => event.type === "user/message" && event.data.source.kind === "user");
|
|
377
|
+
}
|
|
378
|
+
selectionLocked(header, events) {
|
|
379
|
+
const history = events ?? this.ctx.sessions.get(header.id)?.snapshotEvents();
|
|
380
|
+
return history === void 0 || header.isSeeded === true || header.parentSession !== void 0 || isSubagent(header) || this.unbound(header, history) && !sessionEligible(this.ctx, header, history) || sessionHasWork(history);
|
|
381
|
+
}
|
|
382
|
+
view(header, events) {
|
|
383
|
+
if (events !== void 0) restoreSessionBinding(header, events);
|
|
384
|
+
const state = this.syncs.get(header.id);
|
|
385
|
+
const available = this.allowed(header);
|
|
386
|
+
const unbound = this.unbound(header, events);
|
|
387
|
+
const key = unbound ? "" : this.key(header);
|
|
388
|
+
const active = unbound ? void 0 : this.forSession(header);
|
|
389
|
+
const current = this.config(key);
|
|
390
|
+
const configurationChanged = active !== void 0 && current !== void 0 && JSON.stringify(active.team) !== JSON.stringify(current.team);
|
|
391
|
+
let message = state?.message ?? "";
|
|
392
|
+
if (unbound) message = this.selectionLocked(header, events) ? unboundSession : "本会话尚未启用 ACN,可直接聊天,或在首条消息前选择工作配置。";
|
|
393
|
+
else if (!available) message = !this.selectionLocked(header, events) ? "请从 ACN 菜单选择可用的工作配置,初始化当前空白会话。" : "工作配置已停用、缺失或 Agent ID 已改变;请在设置中恢复同一配置及原 Agent ID,或使用新会话。";
|
|
394
|
+
else if (configurationChanged && message === "") message = "已保存新连接,当前会话仍使用启动时的配置。";
|
|
395
|
+
else if (message === "" && this.selected(header)?.prepared === false) message = "上次同步未完成,可继续本地工作或重试同步。";
|
|
396
|
+
return {
|
|
397
|
+
sessionId: header.id,
|
|
398
|
+
key,
|
|
399
|
+
name: this.choices().find((item) => item.key === key)?.name ?? "选择工作配置",
|
|
400
|
+
activeMode: active?.team.routerEndpoint ? "team" : "solo",
|
|
401
|
+
configurationChanged,
|
|
402
|
+
available,
|
|
403
|
+
locked: this.selectionLocked(header, events),
|
|
404
|
+
explicitSelection: this.selected(header) !== void 0,
|
|
405
|
+
needsPreparation: this.syncTasks.has(header.id),
|
|
406
|
+
phase: unbound ? "unbound" : !available ? "blocked" : this.syncTasks.has(header.id) ? "syncing" : state?.phase ?? (this.selected(header)?.prepared === false ? "failed" : "ready"),
|
|
407
|
+
message,
|
|
408
|
+
choices: this.choices(),
|
|
409
|
+
defaultConnection: this.defaultFor(),
|
|
410
|
+
showCompletion: this.base.config.showCompletion
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
async select(header, key) {
|
|
414
|
+
await this.pending;
|
|
415
|
+
if (this.selectionLocked(header)) throw new JobError("bound", "会话开始后不能切换知识归属,请新建会话。");
|
|
416
|
+
if (this.syncs.get(header.id)?.phase === "syncing") throw new JobError("busy", "收件同步仍在进行,可取消等待后重试。");
|
|
417
|
+
const config = this.config(key);
|
|
418
|
+
if (config === void 0 || config.agentId === "") throw new JobError("connection", "请先配置可用身份和团队连接。");
|
|
419
|
+
const attached = this.attached.get(header.id);
|
|
420
|
+
const previous = attached === void 0 ? void 0 : this.ready.get(attached);
|
|
421
|
+
if (previous !== void 0) await previous.catalog.flush(header.id);
|
|
422
|
+
if (this.selectionLocked(header)) throw new JobError("bound", "会话开始后不能切换知识归属,请新建会话。");
|
|
423
|
+
const session = this.ctx.sessions.get(header.id);
|
|
424
|
+
if (session === void 0) throw new JobError("session", "会话已关闭,请重新打开。");
|
|
425
|
+
if (previous !== void 0) {
|
|
426
|
+
previous.formation.disposed(header.id);
|
|
427
|
+
previous.catalog.clear(header.id);
|
|
428
|
+
this.attached.delete(header.id);
|
|
429
|
+
}
|
|
430
|
+
appendPluginRecord(session, "plugin:acn/team-selection", {
|
|
431
|
+
schemaVersion: 1,
|
|
432
|
+
key,
|
|
433
|
+
identity: identityOf(config),
|
|
434
|
+
prepared: config.team.routerEndpoint === ""
|
|
435
|
+
});
|
|
436
|
+
rememberConfiguration(header, config);
|
|
437
|
+
this.prefer(header);
|
|
438
|
+
this.instructions.delete(header.id);
|
|
439
|
+
if (!this.isBase(key)) await this.ensure(key, header.cwd, config);
|
|
440
|
+
if (config.team.routerEndpoint !== "") await this.sync(header);
|
|
441
|
+
else this.syncs.set(header.id, {
|
|
442
|
+
phase: "ready",
|
|
443
|
+
message: "单人模式已就绪,不连接团队服务。"
|
|
444
|
+
});
|
|
445
|
+
if (config.team.routerEndpoint === "" && this.syncs.get(header.id)?.phase !== "failed" && this.key(header) === key) if (this.isBase(key)) for (const refresh of this.baselines) await refresh(header.id);
|
|
446
|
+
else await this.attach(header, "clear");
|
|
447
|
+
return this.view(header);
|
|
448
|
+
}
|
|
449
|
+
async cancel(header) {
|
|
450
|
+
const preparing = this.syncTasks.get(header.id);
|
|
451
|
+
if (preparing === void 0) return;
|
|
452
|
+
const state = this.syncs.get(header.id);
|
|
453
|
+
state?.controller?.abort();
|
|
454
|
+
if (state !== void 0) state.message = "正在结束同步并准备本地知识,已持久化的收件保留。";
|
|
455
|
+
await preparing;
|
|
456
|
+
}
|
|
457
|
+
sync(header) {
|
|
458
|
+
if (this.syncTasks.has(header.id)) return Promise.reject(new JobError("busy", "收件同步仍在进行。"));
|
|
459
|
+
const task = this.synchronize(header);
|
|
460
|
+
this.syncTasks.set(header.id, task);
|
|
461
|
+
const finish = () => {
|
|
462
|
+
if (this.syncTasks.get(header.id) === task) this.syncTasks.delete(header.id);
|
|
463
|
+
};
|
|
464
|
+
task.then(finish, finish);
|
|
465
|
+
return task;
|
|
466
|
+
}
|
|
467
|
+
async synchronize(header) {
|
|
468
|
+
const hadUserAtStart = this.hasUser(header);
|
|
469
|
+
if (this.syncs.get(header.id)?.phase === "syncing") throw new JobError("busy", "收件同步仍在进行。");
|
|
470
|
+
const selectedKey = this.key(header);
|
|
471
|
+
if (this.base.config.upstreams === void 0 && selectedKey === "personal") throw new JobError("connection", "单人模式没有团队收件箱。");
|
|
472
|
+
const controller = new AbortController();
|
|
473
|
+
const state = {
|
|
474
|
+
phase: "syncing",
|
|
475
|
+
message: "正在同步团队收件并准备本地知识…",
|
|
476
|
+
controller
|
|
477
|
+
};
|
|
478
|
+
this.syncs.set(header.id, state);
|
|
479
|
+
try {
|
|
480
|
+
const services = await this.service(header);
|
|
481
|
+
if (services.inbox === void 0 || services.knowledge.jobs === null) throw new JobError("unavailable", "收件处理器不可用,请启用对应插件组件。");
|
|
482
|
+
const agent = this.ctx.agents.get(header.id);
|
|
483
|
+
if (!services.knowledge.config.llm.provider && agent !== void 0) {
|
|
484
|
+
let pending;
|
|
485
|
+
let last;
|
|
486
|
+
for (const event of agent.session.snapshotEvents()) {
|
|
487
|
+
if (event.type === "model/selection") pending = {
|
|
488
|
+
provider: event.data.provider,
|
|
489
|
+
model: event.data.model
|
|
490
|
+
};
|
|
491
|
+
if (event.type === "request/header") {
|
|
492
|
+
last = {
|
|
493
|
+
provider: event.data.header.config.provider,
|
|
494
|
+
model: event.data.header.config.model
|
|
495
|
+
};
|
|
496
|
+
if (pending?.provider === last.provider && pending.model === last.model) pending = void 0;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const route = pending ?? last ?? RouteSchema.safeParse({
|
|
500
|
+
provider: agent.options.provider,
|
|
501
|
+
model: agent.options.model
|
|
502
|
+
}).data;
|
|
503
|
+
if (route !== void 0) {
|
|
504
|
+
const context = services.knowledge.context();
|
|
505
|
+
await withFileLock(`${context.paths.lastRoute}.lock`, () => writeYamlAtomic(context.paths.lastRoute, route, RouteSchema, context.logger));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
let routerFailed = false;
|
|
509
|
+
if (services.knowledge.team !== null) {
|
|
510
|
+
const team = services.knowledge.team;
|
|
511
|
+
try {
|
|
512
|
+
await team.request("router", () => team.router.overview(controller.signal));
|
|
513
|
+
} catch {
|
|
514
|
+
routerFailed = true;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const report = await services.inbox.request(controller.signal, header.id);
|
|
518
|
+
const deadline = Date.now() + services.knowledge.config.llm.timeoutMs;
|
|
519
|
+
for (;;) {
|
|
520
|
+
controller.signal.throwIfAborted();
|
|
521
|
+
const job = (await services.knowledge.jobs.queue.list()).find((item) => item.id === report.jobId);
|
|
522
|
+
if (job?.state === "failed") throw new JobError("inbox", job.lastError ?? "收件内化失败,请检查知识模型后重试。");
|
|
523
|
+
if (job?.state === "succeeded") break;
|
|
524
|
+
if (Date.now() >= deadline) throw new JobError("timeout", "收件处理仍在后台进行,请稍后重试同步并查看处理活动。");
|
|
525
|
+
await new Promise((resolve) => {
|
|
526
|
+
const finish = () => {
|
|
527
|
+
clearTimeout(timer);
|
|
528
|
+
controller.signal.removeEventListener("abort", finish);
|
|
529
|
+
resolve();
|
|
530
|
+
};
|
|
531
|
+
const timer = setTimeout(finish, 100);
|
|
532
|
+
controller.signal.addEventListener("abort", finish, { once: true });
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
controller.signal.throwIfAborted();
|
|
536
|
+
if (this.key(header) !== selectedKey) throw new JobError("superseded", "同步已被新的选择取代。");
|
|
537
|
+
controller.signal.throwIfAborted();
|
|
538
|
+
const session = this.ctx.sessions.get(header.id);
|
|
539
|
+
const selection = this.selected(header);
|
|
540
|
+
if (session !== void 0 && selection !== void 0 && this.key(header) === selectedKey) appendPluginRecord(session, "plugin:acn/team-selection", {
|
|
541
|
+
...selection,
|
|
542
|
+
prepared: true
|
|
543
|
+
});
|
|
544
|
+
state.phase = routerFailed || report.warnings.length > 0 ? "failed" : "ready";
|
|
545
|
+
state.message = [
|
|
546
|
+
routerFailed ? "Router 概览暂不可用。" : "",
|
|
547
|
+
report.warnings.length > 0 ? "Maintainer 收件或确认未完成。" : `收件同步完成:拉取 ${report.pulled} 条。`,
|
|
548
|
+
"本地收件已处理,可继续使用本地知识。"
|
|
549
|
+
].filter(Boolean).join(" ");
|
|
550
|
+
} catch (error) {
|
|
551
|
+
state.phase = "failed";
|
|
552
|
+
state.message = controller.signal.aborted ? "已取消同步等待,已持久化的收件保留,可继续使用本地知识。" : (error instanceof JobError ? error.message : "同步失败,请检查 Router、Maintainer 地址、团队鉴权密钥、知识模型和本地存储后重试。") + " 可继续使用本地知识。";
|
|
553
|
+
} finally {
|
|
554
|
+
delete state.controller;
|
|
555
|
+
const selectedConfig = this.forSession(header);
|
|
556
|
+
const missingAttachment = !this.isBase(this.key(header)) && selectedConfig !== void 0 && this.attached.get(header.id) !== this.runtimeId(this.key(header), selectedConfig);
|
|
557
|
+
if (this.allowed(header) && (!hadUserAtStart || missingAttachment)) if (this.isBase(this.key(header))) for (const refresh of this.baselines) await refresh(header.id, state.phase !== "failed");
|
|
558
|
+
else await this.attach(header, hadUserAtStart ? "resume" : "clear", false);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
ensure(key, workspace, selected) {
|
|
562
|
+
const config = selected ?? this.config(key);
|
|
563
|
+
if (config === void 0 || this.closed) return Promise.reject(new JobError("connection", "团队连接不可用。"));
|
|
564
|
+
const runtimeId = this.runtimeId(key, config);
|
|
565
|
+
const previous = this.runtimes.get(runtimeId);
|
|
566
|
+
const ready = this.ready.get(runtimeId);
|
|
567
|
+
const ownsCurrent = this.preferredWorkers.get(identityOf(config)) === runtimeId;
|
|
568
|
+
if (previous !== void 0 && (!ownsCurrent || ready === void 0 || !this.retiredWorkers.has(ready))) return previous;
|
|
569
|
+
const creating = this.starting.then(async () => {
|
|
570
|
+
if (ready !== void 0) {
|
|
571
|
+
this.ready.delete(runtimeId);
|
|
572
|
+
await ready.close();
|
|
573
|
+
this.retiredWorkers.delete(ready);
|
|
574
|
+
}
|
|
575
|
+
return this.create(config, workspace, ownsCurrent);
|
|
576
|
+
});
|
|
577
|
+
this.starting = creating.then(() => {}, () => {});
|
|
578
|
+
this.runtimes.set(runtimeId, creating);
|
|
579
|
+
creating.then((runtime) => {
|
|
580
|
+
this.ready.set(runtimeId, runtime);
|
|
581
|
+
}, () => {});
|
|
582
|
+
creating.catch(() => {
|
|
583
|
+
if (this.runtimes.get(runtimeId) === creating) this.runtimes.delete(runtimeId);
|
|
584
|
+
});
|
|
585
|
+
return creating;
|
|
586
|
+
}
|
|
587
|
+
async create(config, workspace, current = true) {
|
|
588
|
+
const knowledge = new AcnKnowledge(config, this.ctx.logger, teamCredentials(this.ctx));
|
|
589
|
+
await knowledge.initialize();
|
|
590
|
+
if (workspace !== void 0) await writeYamlAtomic(knowledge.context().paths.dreamWorkspace, workspace, z.string(), knowledge.logger);
|
|
591
|
+
const render = await loadPromptRenderer();
|
|
592
|
+
this.render = render;
|
|
593
|
+
const backend = structuredBackend(this.ctx);
|
|
594
|
+
const catalog = new KnowledgeSessions(knowledge);
|
|
595
|
+
const replay = formationReplay(this.ctx, config);
|
|
596
|
+
const formation = new AcnFormation(knowledge, backend, render, {}, knowledge.jobs ?? void 0, async (route) => {
|
|
597
|
+
try {
|
|
598
|
+
await this.ctx.llm.prepareCall(route, AbortSignal.timeout(15e3));
|
|
599
|
+
return true;
|
|
600
|
+
} catch {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
}, replay);
|
|
604
|
+
const inbox = new AcnInbox(knowledge, backend, render);
|
|
605
|
+
const dream = new AcnDream(knowledge, backend, render, () => this.pending, {
|
|
606
|
+
workspace: () => readYamlOr(knowledge.context().paths.dreamWorkspace, z.string(), () => process.cwd()),
|
|
607
|
+
run: autonomousDreamRunner(this.ctx)
|
|
608
|
+
});
|
|
609
|
+
const uploads = new UploadScheduler(knowledge);
|
|
610
|
+
const runtime = {
|
|
611
|
+
knowledge,
|
|
612
|
+
formation,
|
|
613
|
+
inbox,
|
|
614
|
+
dream,
|
|
615
|
+
catalog,
|
|
616
|
+
uploads,
|
|
617
|
+
components: /* @__PURE__ */ new Set(),
|
|
618
|
+
setComponent: async (component, enabled) => {
|
|
619
|
+
if (runtime.components.has(component) === enabled) return;
|
|
620
|
+
if (enabled) {
|
|
621
|
+
if (component === "formation") {
|
|
622
|
+
runtime.formation = new AcnFormation(knowledge, backend, render, {}, knowledge.jobs ?? void 0, void 0, replay);
|
|
623
|
+
await runtime.formation.start();
|
|
624
|
+
}
|
|
625
|
+
if (component === "inbox") {
|
|
626
|
+
runtime.inbox = new AcnInbox(knowledge, backend, render);
|
|
627
|
+
await runtime.inbox.start();
|
|
628
|
+
}
|
|
629
|
+
if (component === "dream") {
|
|
630
|
+
runtime.dream = new AcnDream(knowledge, backend, render, () => this.pending, {
|
|
631
|
+
workspace: () => readYamlOr(knowledge.context().paths.dreamWorkspace, z.string(), () => process.cwd()),
|
|
632
|
+
run: autonomousDreamRunner(this.ctx)
|
|
633
|
+
});
|
|
634
|
+
await runtime.dream.start();
|
|
635
|
+
}
|
|
636
|
+
runtime.components.add(component);
|
|
637
|
+
for (const agent of this.ctx.agents.list()) if (this.allowed(agent.session.header) && this.readyFor(agent.session.header) === runtime) await this.attach(agent.session.header, "resume", false);
|
|
638
|
+
} else {
|
|
639
|
+
runtime.components.delete(component);
|
|
640
|
+
if (component === "formation") await runtime.formation.close();
|
|
641
|
+
if (component === "inbox") await runtime.inbox.close();
|
|
642
|
+
if (component === "dream") await runtime.dream.close();
|
|
643
|
+
if (component === "prompt") await runtime.catalog.flush();
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
close: async () => {
|
|
647
|
+
await runtime.formation.close();
|
|
648
|
+
await runtime.inbox.close();
|
|
649
|
+
await runtime.dream.close();
|
|
650
|
+
await catalog.flush();
|
|
651
|
+
await uploads.close();
|
|
652
|
+
await knowledge.jobs?.close();
|
|
653
|
+
await knowledge.team?.close();
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
try {
|
|
657
|
+
const identity = identityOf(config);
|
|
658
|
+
const previous = this.workers.get(identity);
|
|
659
|
+
if (previous !== void 0) if (current) {
|
|
660
|
+
await previous.uploads.close();
|
|
661
|
+
await previous.knowledge.jobs?.close();
|
|
662
|
+
this.retiredWorkers.add(previous);
|
|
663
|
+
} else {
|
|
664
|
+
await knowledge.jobs?.close();
|
|
665
|
+
await uploads.close();
|
|
666
|
+
this.retiredWorkers.add(runtime);
|
|
667
|
+
}
|
|
668
|
+
if (current || previous === void 0) this.workers.set(identity, runtime);
|
|
669
|
+
await this.ctx.inject(["workspaceRegistry"], (child) => connectArchives(child, {
|
|
670
|
+
beginArchive: (id, upper) => runtime.components.has("formation") ? runtime.formation.beginArchive(id, upper) : Promise.resolve(null),
|
|
671
|
+
cancelArchive: (id) => runtime.formation.cancelArchive(id),
|
|
672
|
+
archived: (id, upper) => runtime.formation.archived(id, upper),
|
|
673
|
+
reconcileArchives: (ids) => runtime.formation.reconcileArchives(ids)
|
|
674
|
+
}, formationOwnership(child, config)));
|
|
675
|
+
for (const name of [
|
|
676
|
+
"prompt",
|
|
677
|
+
"formation",
|
|
678
|
+
"inbox",
|
|
679
|
+
"dream"
|
|
680
|
+
]) if (this.enabled(name)) {
|
|
681
|
+
runtime.components.add(name);
|
|
682
|
+
if (name === "formation") await formation.start();
|
|
683
|
+
if (name === "inbox") await inbox.start();
|
|
684
|
+
if (name === "dream") await dream.start();
|
|
685
|
+
} else {
|
|
686
|
+
if (name === "formation") await formation.close();
|
|
687
|
+
if (name === "inbox") await inbox.close();
|
|
688
|
+
if (name === "dream") await dream.close();
|
|
689
|
+
}
|
|
690
|
+
await knowledge.jobs?.start();
|
|
691
|
+
uploads.start();
|
|
692
|
+
return runtime;
|
|
693
|
+
} catch (error) {
|
|
694
|
+
await runtime.close();
|
|
695
|
+
throw error;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async freezeInstructions(header, knowledge) {
|
|
699
|
+
if (this.base.config.upstreams === void 0 || this.instructions.has(header.id)) return;
|
|
700
|
+
const file = join(knowledge.context().paths.session(header.id), "upstream-instructions.yaml");
|
|
701
|
+
let text = await readYamlOr(file, z.string(), () => null);
|
|
702
|
+
if (text === null && header.parentSession !== void 0) text = await readYamlOr(join(knowledge.context().paths.session(header.parentSession), "upstream-instructions.yaml"), z.string(), () => null);
|
|
703
|
+
if (text === null) {
|
|
704
|
+
text = "";
|
|
705
|
+
if (!(this.ctx.sessions.get(header.id)?.snapshotEvents().some((event) => event.type === "request/header") ?? true)) try {
|
|
706
|
+
text = await fs.readFile(join(resolve(agentHomePath(this.base.config.home, "example"), "..", ".."), this.key(header), "ACN.md"), "utf8");
|
|
707
|
+
} catch (error) {
|
|
708
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
|
|
709
|
+
}
|
|
710
|
+
await writeYamlAtomic(file, text, z.string(), knowledge.logger);
|
|
711
|
+
}
|
|
712
|
+
this.instructions.set(header.id, text);
|
|
713
|
+
}
|
|
714
|
+
async bind(header) {
|
|
715
|
+
if (this.base.config.upstreams === void 0 || isEvidenceAgent(header)) return;
|
|
716
|
+
const session = this.ctx.sessions.get(header.id);
|
|
717
|
+
if (session === void 0) return;
|
|
718
|
+
let selection = this.selected(header);
|
|
719
|
+
if (selection === void 0 && header.parentSession !== void 0) {
|
|
720
|
+
const parent = this.ctx.sessions.get(header.parentSession);
|
|
721
|
+
if (parent !== void 0 && this.allowed(parent.header)) selection = this.selected(parent.header);
|
|
722
|
+
}
|
|
723
|
+
if (selection === void 0) {
|
|
724
|
+
restoreSessionBinding(header, session.snapshotEvents());
|
|
725
|
+
const key = this.choices().find((choice) => {
|
|
726
|
+
const config = this.config(choice.key);
|
|
727
|
+
return config !== void 0 && config.agentId !== "" && sessionAllowed(this.ctx, config, header);
|
|
728
|
+
})?.key;
|
|
729
|
+
const config = key === void 0 ? void 0 : this.config(key);
|
|
730
|
+
if (key !== void 0 && config !== void 0) selection = {
|
|
731
|
+
schemaVersion: 1,
|
|
732
|
+
key,
|
|
733
|
+
identity: identityOf(config),
|
|
734
|
+
prepared: true
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
if (selection === void 0 && session.firstLiveSeq === 0 && sessionEligible(this.ctx, header) && !sessionHasWork(session.snapshotEvents()) && !header.isSeeded && header.parentSession === void 0) {
|
|
738
|
+
const recent = await recentProjectSelection(this.ctx, header);
|
|
739
|
+
const previous = recent === void 0 ? void 0 : this.config(recent.key);
|
|
740
|
+
const key = recent !== void 0 && previous !== void 0 && recent.identity === identityOf(previous) ? recent.key : this.defaultFor();
|
|
741
|
+
const config = this.config(key);
|
|
742
|
+
if (config !== void 0) selection = {
|
|
743
|
+
schemaVersion: 1,
|
|
744
|
+
key,
|
|
745
|
+
identity: identityOf(config),
|
|
746
|
+
prepared: true
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
if (selection === void 0) return;
|
|
750
|
+
if (this.selected(header) === void 0) {
|
|
751
|
+
appendPluginRecord(session, "plugin:acn/team-selection", selection);
|
|
752
|
+
restoreSessionBinding(header, session.snapshotEvents());
|
|
753
|
+
}
|
|
754
|
+
const config = this.config(selection.key);
|
|
755
|
+
if (config !== void 0 && identityOf(config) === selection.identity && runningConfiguration(header) === void 0) rememberConfiguration(header, config);
|
|
756
|
+
}
|
|
757
|
+
async attach(header, source, synchronize = true, signal) {
|
|
758
|
+
signal?.throwIfAborted();
|
|
759
|
+
if (!this.allowed(header) || this.isBase(this.key(header)) || isEvidenceAgent(header)) return;
|
|
760
|
+
const ready = this.ensure(this.key(header), header.cwd, this.forSession(header));
|
|
761
|
+
const runtime = signal === void 0 ? await ready : await abortable(ready, signal);
|
|
762
|
+
if (synchronize && this.base.config.upstreams !== void 0 && (source === "startup" || source === "resume") && !isSubagent(header) && runtime.components.has("inbox") && runtime.knowledge.config.inbox.pullOnSessionStart) {
|
|
763
|
+
await this.sync(header);
|
|
764
|
+
if (!this.hasUser(header)) return;
|
|
765
|
+
}
|
|
766
|
+
await this.freezeInstructions(header, runtime.knowledge);
|
|
767
|
+
if (runtime.components.has("prompt")) await runtime.catalog.freeze(header.id, source, header.parentSession, this.syncs.get(header.id)?.phase !== "failed", signal);
|
|
768
|
+
if (!isSubagent(header)) {
|
|
769
|
+
if (runtime.components.has("formation")) {
|
|
770
|
+
runtime.formation.attach(header.id);
|
|
771
|
+
if (this.ctx.agents.get(header.id)?.status === "running") runtime.formation.running(header.id);
|
|
772
|
+
}
|
|
773
|
+
if (header.cwd !== void 0) await writeYamlAtomic(runtime.knowledge.context().paths.dreamWorkspace, header.cwd, z.string(), runtime.knowledge.logger);
|
|
774
|
+
if (runtime.components.has("dream")) runtime.dream.trigger();
|
|
775
|
+
}
|
|
776
|
+
this.attached.set(header.id, this.runtimeId(this.key(header), runtime.knowledge.config));
|
|
777
|
+
}
|
|
778
|
+
async prepareSession(header, signal) {
|
|
779
|
+
signal?.throwIfAborted();
|
|
780
|
+
const preparing = Promise.all([this.pending, this.syncTasks.get(header.id)]);
|
|
781
|
+
if (signal === void 0) await preparing;
|
|
782
|
+
else await abortable(preparing, signal);
|
|
783
|
+
signal?.throwIfAborted();
|
|
784
|
+
if (this.unbound(header)) {
|
|
785
|
+
const agent = this.ctx.agents.get(header.id);
|
|
786
|
+
const deny = ["claim", "consult_router"].filter((name) => this.ctx.tools.get(name) !== void 0);
|
|
787
|
+
const previous = this.unboundTools.get(header.id);
|
|
788
|
+
if (agent !== void 0 && previous?.names !== deny.join(",")) {
|
|
789
|
+
previous?.release();
|
|
790
|
+
this.unboundTools.delete(header.id);
|
|
791
|
+
if (deny.length > 0) this.unboundTools.set(header.id, {
|
|
792
|
+
names: deny.join(","),
|
|
793
|
+
release: agent.ctx.tools.restrict({ deny })
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
if (!this.allowed(header)) throw new JobError("identity", "会话的知识归属不可用,请恢复原连接或新建会话。");
|
|
799
|
+
if (this.syncs.get(header.id)?.phase === "syncing") throw new JobError("sync", "收件同步仍在进行,可取消等待后继续本地工作。");
|
|
800
|
+
if (!this.isBase(this.key(header))) {
|
|
801
|
+
const ready = this.ensure(this.key(header), header.cwd, this.forSession(header));
|
|
802
|
+
const runtime = signal === void 0 ? await ready : await abortable(ready, signal);
|
|
803
|
+
if (this.attached.get(header.id) !== this.runtimeId(this.key(header), runtime.knowledge.config)) await this.attach(header, "resume", false, signal);
|
|
804
|
+
if (runtime.components.has("prompt") && runtime.catalog.catalog(header.id) === "") await runtime.catalog.freeze(header.id, "resume", header.parentSession, true, signal);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async install(ctx) {
|
|
808
|
+
const forAgent = (header, component) => {
|
|
809
|
+
const runtime = header === void 0 || !this.allowed(header) || this.isBase(this.key(header)) ? void 0 : this.readyFor(header);
|
|
810
|
+
return runtime?.components.has(component) ? runtime : void 0;
|
|
811
|
+
};
|
|
812
|
+
const contributions = [
|
|
813
|
+
{
|
|
814
|
+
name: "acn:upstream-instructions",
|
|
815
|
+
order: 730,
|
|
816
|
+
interpolate: false,
|
|
817
|
+
text: ({ agent }) => agent === void 0 || isEvidenceAgent(agent.session.header) || !this.allowed(agent.session.header) ? "" : this.instructions.get(agent.session.id) ?? ""
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
name: "acn:connection-knowledge",
|
|
821
|
+
order: 700,
|
|
822
|
+
interpolate: false,
|
|
823
|
+
text: ({ agent }) => {
|
|
824
|
+
const runtime = forAgent(agent?.session.header, "prompt");
|
|
825
|
+
return runtime === void 0 ? "" : this.render?.("knowledge.j2", {
|
|
826
|
+
agent_id: runtime.knowledge.agentId,
|
|
827
|
+
team: runtime.knowledge.mode === "team"
|
|
828
|
+
}) ?? "";
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
name: "acn:connection-catalog",
|
|
833
|
+
order: 710,
|
|
834
|
+
interpolate: false,
|
|
835
|
+
text: ({ agent }) => forAgent(agent?.session.header, "prompt")?.catalog.catalog(agent?.session.id ?? "") ?? ""
|
|
836
|
+
}
|
|
837
|
+
];
|
|
838
|
+
for (const section of contributions) ctx.systemPrompt.section(section);
|
|
839
|
+
ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
|
|
840
|
+
const header = context.agent?.session.header;
|
|
841
|
+
if (header === void 0 || isEvidenceAgent(header)) return next();
|
|
842
|
+
if (this.unbound(header)) {
|
|
843
|
+
await this.prepareSession(header, context.signal);
|
|
844
|
+
const assembly = await next();
|
|
845
|
+
return {
|
|
846
|
+
...assembly,
|
|
847
|
+
tools: assembly.tools.filter((tool) => !["claim", "consult_router"].includes(tool.name))
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
if (!this.allowed(header)) return next();
|
|
851
|
+
await this.prepareSession(header, context.signal);
|
|
852
|
+
context.signal?.throwIfAborted();
|
|
853
|
+
const assembly = await next();
|
|
854
|
+
const baseSections = this.isBase(this.key(header)) ? Object.assign({}, ...[...this.baselineRenderers.values()].map((render) => render(header))) : {};
|
|
855
|
+
return {
|
|
856
|
+
...assembly,
|
|
857
|
+
sections: assembly.sections.map((section) => {
|
|
858
|
+
if (Object.hasOwn(baseSections, section.name)) return {
|
|
859
|
+
...section,
|
|
860
|
+
text: baseSections[section.name] ?? ""
|
|
861
|
+
};
|
|
862
|
+
const definition = contributions.find((item) => item.name === section.name);
|
|
863
|
+
return definition === void 0 ? section : {
|
|
864
|
+
...section,
|
|
865
|
+
text: typeof definition.text === "function" ? definition.text(context) : definition.text
|
|
866
|
+
};
|
|
867
|
+
})
|
|
868
|
+
};
|
|
869
|
+
});
|
|
870
|
+
ctx.on("agent/created", async ({ agent, source, signal }) => {
|
|
871
|
+
if (source === "startup") markSessionEligible(ctx, agent.session.header);
|
|
872
|
+
await this.bind(agent.session.header);
|
|
873
|
+
this.prefer(agent.session.header);
|
|
874
|
+
await this.attach(agent.session.header, source, true, signal);
|
|
875
|
+
if (source === "resume") this.resumed.add(agent.session.id);
|
|
876
|
+
});
|
|
877
|
+
ctx.on("agent/pre-step", async ({ agent, signal }, next) => {
|
|
878
|
+
if (isEvidenceAgent(agent.session.header)) return next();
|
|
879
|
+
await this.prepareSession(agent.session.header, signal);
|
|
880
|
+
if (this.unbound(agent.session.header)) return next();
|
|
881
|
+
if (this.resumed.delete(agent.session.id) && this.base.config.upstreams === void 0 && !isSubagent(agent.session.header) && !this.isBase(this.key(agent.session.header))) {
|
|
882
|
+
const runtime = await this.ensure(this.key(agent.session.header), agent.session.header.cwd, this.forSession(agent.session.header));
|
|
883
|
+
if (runtime.components.has("inbox") && runtime.knowledge.mode === "team" && runtime.knowledge.config.inbox.pullOnSessionStart) await runtime.inbox.enqueue(true, agent.session.id);
|
|
884
|
+
}
|
|
885
|
+
const decision = await next();
|
|
886
|
+
if (decision.kind === "reject" || this.isBase(this.key(agent.session.header))) return decision;
|
|
887
|
+
const runtime = await this.ensure(this.key(agent.session.header), agent.session.header.cwd, this.forSession(agent.session.header));
|
|
888
|
+
const notice = runtime.components.has("prompt") ? await runtime.catalog.prepare(agent.session.id, signal) : null;
|
|
889
|
+
signal.throwIfAborted();
|
|
890
|
+
if (notice === null) return decision;
|
|
891
|
+
const message = createUserMessage({
|
|
892
|
+
content: [{
|
|
893
|
+
type: "text",
|
|
894
|
+
text: notice.text
|
|
895
|
+
}],
|
|
896
|
+
source: { kind: "acn-claim-changes" }
|
|
897
|
+
});
|
|
898
|
+
this.admissions.set(message.id, {
|
|
899
|
+
id: agent.session.id,
|
|
900
|
+
runtime,
|
|
901
|
+
notice
|
|
902
|
+
});
|
|
903
|
+
return {
|
|
904
|
+
...decision,
|
|
905
|
+
messages: [...decision.messages, message]
|
|
906
|
+
};
|
|
907
|
+
});
|
|
908
|
+
ctx.on("session/event", (session, event) => {
|
|
909
|
+
const admission = event.type === "user/message" ? this.admissions.get(event.data.id) : void 0;
|
|
910
|
+
if (admission?.id === session.id && event.type === "user/message") {
|
|
911
|
+
this.admissions.delete(event.data.id);
|
|
912
|
+
admission.runtime.catalog.admitted(session.id, admission.notice);
|
|
913
|
+
}
|
|
914
|
+
if (!this.allowed(session.header) || this.isBase(this.key(session.header)) || isSubagent(session.header)) return;
|
|
915
|
+
const runtime = this.readyFor(session.header);
|
|
916
|
+
if (runtime === void 0) return;
|
|
917
|
+
const projection = projectEvent(event);
|
|
918
|
+
const until = compactionEnd(event);
|
|
919
|
+
if (runtime.components.has("formation") && (Object.keys(projection).length > 0 || until !== void 0)) runtime.formation.capture(session.id, projection, until);
|
|
920
|
+
if (projection.route !== void 0) this.schedule(() => withFileLock(`${runtime.knowledge.context().paths.lastRoute}.lock`, () => writeYamlAtomic(runtime.knowledge.context().paths.lastRoute, projection.route, RouteSchema, runtime.knowledge.logger)));
|
|
921
|
+
});
|
|
922
|
+
ctx.on("agent/status", ({ agent, status }) => {
|
|
923
|
+
if (!this.allowed(agent.session.header) || this.isBase(this.key(agent.session.header)) || isSubagent(agent.session.header)) return;
|
|
924
|
+
const runtime = this.readyFor(agent.session.header);
|
|
925
|
+
if (runtime === void 0) return;
|
|
926
|
+
if (status === "running") runtime.formation.running(agent.session.id);
|
|
927
|
+
else runtime.formation.idle(agent.session.id);
|
|
928
|
+
});
|
|
929
|
+
ctx.on("agent/disposed", ({ agent }) => {
|
|
930
|
+
this.unboundTools.get(agent.session.id)?.release();
|
|
931
|
+
this.unboundTools.delete(agent.session.id);
|
|
932
|
+
const key = this.attached.get(agent.session.id);
|
|
933
|
+
if (key === void 0) return;
|
|
934
|
+
const runtime = this.ready.get(key);
|
|
935
|
+
if (runtime === void 0) return;
|
|
936
|
+
runtime.formation.disposed(agent.session.id);
|
|
937
|
+
this.schedule(() => runtime.catalog.forget(agent.session.id));
|
|
938
|
+
this.attached.delete(agent.session.id);
|
|
939
|
+
this.instructions.delete(agent.session.id);
|
|
940
|
+
forgetConfiguration(agent.session.header);
|
|
941
|
+
});
|
|
942
|
+
const existing = ctx.agents.list();
|
|
943
|
+
for (const agent of existing) await this.bind(agent.session.header);
|
|
944
|
+
existing.sort((left, right) => configurationActivation(right.session.header) - configurationActivation(left.session.header));
|
|
945
|
+
for (const agent of existing) {
|
|
946
|
+
const config = this.forSession(agent.session.header);
|
|
947
|
+
if (config !== void 0 && !this.preferredWorkers.has(identityOf(config))) this.prefer(agent.session.header);
|
|
948
|
+
}
|
|
949
|
+
for (const agent of existing) {
|
|
950
|
+
await this.attach(agent.session.header, "resume", false);
|
|
951
|
+
this.resumed.add(agent.session.id);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
schedule(action) {
|
|
955
|
+
if (this.closed) return;
|
|
956
|
+
this.pending = this.pending.then(action).then(() => {}).catch(() => {
|
|
957
|
+
this.ctx.logger.warn("Could not persist a connection runtime event.");
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
async close() {
|
|
961
|
+
this.closed = true;
|
|
962
|
+
for (const entry of this.unboundTools.values()) entry.release();
|
|
963
|
+
this.unboundTools.clear();
|
|
964
|
+
for (const state of this.syncs.values()) state.controller?.abort();
|
|
965
|
+
await this.pending;
|
|
966
|
+
await Promise.allSettled([...this.syncTasks.values()]);
|
|
967
|
+
const runtimes = await Promise.allSettled([...this.runtimes.values()]);
|
|
968
|
+
for (const result of runtimes) if (result.status === "fulfilled") await result.value.close();
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
//#endregion
|
|
972
|
+
//#region src/plugins/knowledge.ts
|
|
973
|
+
const name = "acn-knowledge";
|
|
974
|
+
const inject = [];
|
|
975
|
+
const number = (value) => Schema.number().default(value);
|
|
976
|
+
const boolean = (value) => Schema.boolean().default(value);
|
|
977
|
+
const Config = Schema.object({
|
|
978
|
+
agentId: Schema.string().default(""),
|
|
979
|
+
home: Schema.string().default("~/.acn-dsh"),
|
|
980
|
+
sessionEpoch: number(0),
|
|
981
|
+
connections: Schema.array(Schema.object({
|
|
982
|
+
id: Schema.string(),
|
|
983
|
+
name: Schema.string(),
|
|
984
|
+
enabled: boolean(true),
|
|
985
|
+
agentId: Schema.string(),
|
|
986
|
+
routerEndpoint: Schema.string(),
|
|
987
|
+
maintainerEndpoint: Schema.string(),
|
|
988
|
+
keyEnv: Schema.string().default(""),
|
|
989
|
+
timeoutMs: number(15e3)
|
|
990
|
+
})).default([]),
|
|
991
|
+
upstream: Schema.string().default(""),
|
|
992
|
+
upstreams: Schema.any(),
|
|
993
|
+
defaultConnection: Schema.string().default(""),
|
|
994
|
+
legacyEnabled: boolean(true),
|
|
995
|
+
showCompletion: boolean(true),
|
|
996
|
+
team: Schema.object({
|
|
997
|
+
routerEndpoint: Schema.string().default(""),
|
|
998
|
+
maintainerEndpoint: Schema.string().default(""),
|
|
999
|
+
keyEnv: Schema.string().default(""),
|
|
1000
|
+
timeoutMs: number(15e3),
|
|
1001
|
+
routerTimeoutMs: number(5e4),
|
|
1002
|
+
retryCount: number(3),
|
|
1003
|
+
retryBaseDelayMs: number(500),
|
|
1004
|
+
retryMaxDelayMs: number(8e3)
|
|
1005
|
+
}),
|
|
1006
|
+
llm: Schema.object({
|
|
1007
|
+
provider: Schema.string().default(""),
|
|
1008
|
+
model: Schema.string().default(""),
|
|
1009
|
+
maxOutputTokens: number(32768),
|
|
1010
|
+
timeoutMs: number(6e5),
|
|
1011
|
+
structuredRetries: number(5)
|
|
1012
|
+
}),
|
|
1013
|
+
catalog: Schema.object({
|
|
1014
|
+
maxClaims: number(20),
|
|
1015
|
+
maxChars: number(24e3)
|
|
1016
|
+
}),
|
|
1017
|
+
formation: Schema.object({
|
|
1018
|
+
recapOnCompaction: boolean(true),
|
|
1019
|
+
idleRecap: boolean(true),
|
|
1020
|
+
recapMinTurns: number(10),
|
|
1021
|
+
recapMinTokens: number(1e5),
|
|
1022
|
+
recapBatchIdleMinutes: number(5),
|
|
1023
|
+
recapTailIdleMinutes: number(60),
|
|
1024
|
+
recapScanSeconds: number(30),
|
|
1025
|
+
finalizeIdleMinutes: number(0),
|
|
1026
|
+
finalizeOnDispose: boolean(false)
|
|
1027
|
+
}),
|
|
1028
|
+
inbox: Schema.object({
|
|
1029
|
+
pullOnSessionStart: boolean(true),
|
|
1030
|
+
processingStaleAfterSecs: number(600)
|
|
1031
|
+
}),
|
|
1032
|
+
dream: Schema.object({
|
|
1033
|
+
enabled: boolean(true),
|
|
1034
|
+
minIntervalHours: number(24),
|
|
1035
|
+
minChangedClaims: number(5),
|
|
1036
|
+
checkIntervalHours: number(4),
|
|
1037
|
+
evidenceCalibration: boolean(false),
|
|
1038
|
+
maxModelSteps: number(24),
|
|
1039
|
+
maxWrapUpSteps: number(3),
|
|
1040
|
+
evidenceMaxCalls: number(32),
|
|
1041
|
+
evidenceMaxSteps: number(24),
|
|
1042
|
+
evidenceMaxBytes: number(1048576),
|
|
1043
|
+
evidenceMaxChars: number(16e3),
|
|
1044
|
+
evidenceMaxFiles: number(256)
|
|
1045
|
+
})
|
|
1046
|
+
});
|
|
1047
|
+
async function apply(ctx, input = {}) {
|
|
1048
|
+
const config = resolveConfig(input);
|
|
1049
|
+
const knowledge = new AcnKnowledge(config.upstreams === void 0 ? config : {
|
|
1050
|
+
...config,
|
|
1051
|
+
agentId: ""
|
|
1052
|
+
}, ctx.logger, teamCredentials(ctx));
|
|
1053
|
+
await knowledge.initialize();
|
|
1054
|
+
ctx.provide("acnKnowledge", knowledge);
|
|
1055
|
+
const pool = new RuntimePool(ctx, knowledge);
|
|
1056
|
+
ctx.provide("acnRuntime", pool);
|
|
1057
|
+
ctx.effect(() => () => pool.close());
|
|
1058
|
+
if (knowledge.configured || config.upstreams !== void 0 || config.connections.length > 0) ctx.inject([
|
|
1059
|
+
"fs",
|
|
1060
|
+
"tools",
|
|
1061
|
+
"workingDirectory"
|
|
1062
|
+
], (child) => {
|
|
1063
|
+
protectKnowledgePaths(child, knowledge);
|
|
1064
|
+
});
|
|
1065
|
+
const uploads = knowledge.configured && knowledge.active ? new UploadScheduler(knowledge) : null;
|
|
1066
|
+
ctx.effect(() => {
|
|
1067
|
+
const abort = new AbortController();
|
|
1068
|
+
if (knowledge.active && knowledge.team !== null) knowledge.team.health(abort.signal).catch(() => {
|
|
1069
|
+
ctx.logger.warn("Team health check could not complete.");
|
|
1070
|
+
});
|
|
1071
|
+
uploads?.start();
|
|
1072
|
+
return async () => {
|
|
1073
|
+
abort.abort();
|
|
1074
|
+
await uploads?.close();
|
|
1075
|
+
await knowledge.jobs?.close();
|
|
1076
|
+
await knowledge.team?.close();
|
|
1077
|
+
};
|
|
1078
|
+
});
|
|
1079
|
+
ctx.inject([
|
|
1080
|
+
"agents",
|
|
1081
|
+
"sessions",
|
|
1082
|
+
"tools"
|
|
1083
|
+
], (child) => {
|
|
1084
|
+
child.on("session/event", (session, event) => {
|
|
1085
|
+
observeSelection(session.header, event);
|
|
1086
|
+
});
|
|
1087
|
+
child.on("agent/pre-step", async ({ agent }, next) => {
|
|
1088
|
+
if (!pool.chatAllowed(agent.session.header)) throw new Error(sessionChanged);
|
|
1089
|
+
return next();
|
|
1090
|
+
});
|
|
1091
|
+
child.tools.guard((execution) => ["claim", "consult_router"].includes(execution.name) && execution.agent !== void 0 && !pool.allowed(execution.agent.session.header) ? pool.unbound(execution.agent.session.header) ? unboundSession : sessionChanged : void 0);
|
|
1092
|
+
child.on("agent/created", ({ agent }) => {
|
|
1093
|
+
if (config.upstreams === void 0) {
|
|
1094
|
+
bindSession(child, knowledge.config, agent.session.header);
|
|
1095
|
+
if (runningConfiguration(agent.session.header) === void 0 && sessionAllowed(child, knowledge.config, agent.session.header)) rememberConfiguration(agent.session.header, knowledge.config);
|
|
1096
|
+
}
|
|
1097
|
+
uploads?.kick();
|
|
1098
|
+
});
|
|
1099
|
+
if (config.upstreams === void 0) for (const agent of child.agents.list()) bindSession(child, knowledge.config, agent.session.header);
|
|
1100
|
+
});
|
|
1101
|
+
if (knowledge.active) await knowledge.jobs?.start();
|
|
1102
|
+
ctx.logger.info("ACN knowledge plugin activated.");
|
|
1103
|
+
}
|
|
1104
|
+
var knowledge_default = {
|
|
1105
|
+
name,
|
|
1106
|
+
Config,
|
|
1107
|
+
inject,
|
|
1108
|
+
apply
|
|
1109
|
+
};
|
|
1110
|
+
//#endregion
|
|
1111
|
+
export { Config, apply, knowledge_default as default, inject, name };
|