@akagilnc/pi-workflow-roles 0.1.3722 → 0.1.3737
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/acp-host/description.js +19 -5
- package/dist/acp-host/production-host.js +157 -50
- package/dist/acp-host/seat-profile-soul.js +78 -0
- package/dist/host-descriptions.js +26 -0
- package/dist/public-cli/main.js +26 -0
- package/package.json +1 -1
- package/souls/countersign.md +1 -1
- package/souls/judge.md +21 -68
- package/souls/notary.md +1 -1
- package/src/acp-host/description.ts +39 -4
- package/src/acp-host/production-host.ts +16 -1
- package/src/acp-host/role-envelope.ts +2 -1
- package/src/acp-host/role-turn-host.ts +47 -23
- package/src/acp-host/seat-profile-soul.ts +97 -0
- package/src/host-descriptions.ts +26 -0
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* One ACP host description. Every host-specific value the generic ACP adapter
|
|
3
|
-
* needs — binary location, argv shape, resume verb, binding filename, child env
|
|
4
|
-
* — is data here; the lifecycle in role-turn-host.ts
|
|
3
|
+
* needs — binary location, argv shape, resume verb, binding filename, child env,
|
|
4
|
+
* optional seat-profile soul — is data here; the lifecycle in role-turn-host.ts
|
|
5
|
+
* stays one copy (#732).
|
|
5
6
|
*/
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
/** Absolute agent binary for one operator home. */
|
|
8
9
|
export function resolveAcpBinary(description, operatorHome) {
|
|
9
10
|
return join(operatorHome, ...description.binaryFromHome);
|
|
10
11
|
}
|
|
11
|
-
/** Stdio argv:
|
|
12
|
-
|
|
12
|
+
/** Stdio argv: optional profile flag, thinking flag (before the subcommand),
|
|
13
|
+
* prefix, optional model flag pair, suffix. */
|
|
14
|
+
export function acpStdioArgs(description, model, seat) {
|
|
13
15
|
const { prefix, suffix, modelFlag, thinkingFlag } = description.argv;
|
|
14
16
|
const pair = (flag, value) => flag === undefined || value === undefined ? [] : [flag, value];
|
|
15
17
|
return [
|
|
18
|
+
...pair(description.seatProfileSoul?.flag, seat?.profileName),
|
|
19
|
+
...pair(thinkingFlag, model?.thinking),
|
|
16
20
|
...prefix,
|
|
17
21
|
...pair(modelFlag, model?.model),
|
|
18
|
-
...pair(thinkingFlag, model?.thinking),
|
|
19
22
|
...suffix,
|
|
20
23
|
];
|
|
21
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The modelId the host addresses the seat model by.
|
|
27
|
+
* "argv" hosts address by bare model name; "set_model" hosts address by the
|
|
28
|
+
* `provider:model` modelId the ACP catalog exposes (seat provider after host
|
|
29
|
+
* alias projection, concatenated — never a package provider map).
|
|
30
|
+
*/
|
|
31
|
+
export function acpModelId(modelPassing, model) {
|
|
32
|
+
if (model?.model === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
return modelPassing === "set_model" ? `${model.provider}:${model.model}` : model.model;
|
|
35
|
+
}
|
|
@@ -164,7 +164,7 @@ function normalizeReviewComment(raw) {
|
|
|
164
164
|
function createGhApiRunner(options = {}) {
|
|
165
165
|
const spawnImpl = options.spawnImpl ?? spawn;
|
|
166
166
|
return async (args, runOptions = {}) => {
|
|
167
|
-
return await new Promise((
|
|
167
|
+
return await new Promise((resolve20, reject) => {
|
|
168
168
|
const signal = runOptions.signal;
|
|
169
169
|
if (signal?.aborted) {
|
|
170
170
|
reject(signal.reason ?? new Error("aborted"));
|
|
@@ -237,11 +237,11 @@ function createGhApiRunner(options = {}) {
|
|
|
237
237
|
const value = line2.slice(idx + 1).trim();
|
|
238
238
|
headers[name] = value;
|
|
239
239
|
}
|
|
240
|
-
|
|
240
|
+
resolve20({ status, headers, bodyText });
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
243
243
|
if (code === 0) {
|
|
244
|
-
|
|
244
|
+
resolve20({ status: 200, headers: {}, bodyText: stdout });
|
|
245
245
|
return;
|
|
246
246
|
}
|
|
247
247
|
const failure2 = new Error(
|
|
@@ -1227,7 +1227,7 @@ async function spawnEngineDetourOnce(input) {
|
|
|
1227
1227
|
}
|
|
1228
1228
|
const command = input.argv[0];
|
|
1229
1229
|
const args = input.argv.slice(1);
|
|
1230
|
-
return await new Promise((
|
|
1230
|
+
return await new Promise((resolve20, reject) => {
|
|
1231
1231
|
let settled = false;
|
|
1232
1232
|
const signal = input.signal;
|
|
1233
1233
|
const child = spawn2(command, args, {
|
|
@@ -1257,7 +1257,7 @@ async function spawnEngineDetourOnce(input) {
|
|
|
1257
1257
|
if (signal !== void 0) {
|
|
1258
1258
|
signal.removeEventListener("abort", onAbort);
|
|
1259
1259
|
}
|
|
1260
|
-
|
|
1260
|
+
resolve20(result);
|
|
1261
1261
|
};
|
|
1262
1262
|
const onAbort = () => {
|
|
1263
1263
|
fail5(signal !== void 0 ? abortReasonError(signal) : new Error("aborted"));
|
|
@@ -8398,6 +8398,7 @@ var init_host_descriptions = __esm({
|
|
|
8398
8398
|
suffix: Object.freeze(["stdio"]),
|
|
8399
8399
|
modelFlag: "--model"
|
|
8400
8400
|
}),
|
|
8401
|
+
modelPassing: "argv",
|
|
8401
8402
|
boundResume: "session/load",
|
|
8402
8403
|
sessionBindingFile: "grok-acp-session.json",
|
|
8403
8404
|
childEnv: Object.freeze({
|
|
@@ -8405,6 +8406,31 @@ var init_host_descriptions = __esm({
|
|
|
8405
8406
|
GROK_MEMORY: "0",
|
|
8406
8407
|
GROK_SUBAGENTS: "0"
|
|
8407
8408
|
})
|
|
8409
|
+
}),
|
|
8410
|
+
/**
|
|
8411
|
+
* Operator home `~/.hermes`, native session/load resume, `acp` subcommand.
|
|
8412
|
+
* Model arrives as an ACP `session/set_model` RPC with modelId `provider:model`
|
|
8413
|
+
* (seat table provider + model concatenated). Reasoning is the global
|
|
8414
|
+
* `--reasoning` flag before `acp`. Soul is the seat profile SOUL.md symlink
|
|
8415
|
+
* (`hermes -p ak-<role> …`); package `souls/<role>.md` is the sole source.
|
|
8416
|
+
*/
|
|
8417
|
+
"hermes": Object.freeze({
|
|
8418
|
+
binaryFromHome: Object.freeze([".local", "bin", "hermes"]),
|
|
8419
|
+
argv: Object.freeze({
|
|
8420
|
+
prefix: Object.freeze(["acp"]),
|
|
8421
|
+
suffix: Object.freeze([]),
|
|
8422
|
+
thinkingFlag: "--reasoning"
|
|
8423
|
+
}),
|
|
8424
|
+
modelPassing: "set_model",
|
|
8425
|
+
boundResume: "session/load",
|
|
8426
|
+
sessionBindingFile: "hermes-acp-session.json",
|
|
8427
|
+
childEnv: Object.freeze({}),
|
|
8428
|
+
seatProfileSoul: Object.freeze({
|
|
8429
|
+
flag: "-p",
|
|
8430
|
+
namePrefix: "ak-",
|
|
8431
|
+
profilesRootFromHome: Object.freeze([".hermes", "profiles"]),
|
|
8432
|
+
soulFileName: "SOUL.md"
|
|
8433
|
+
})
|
|
8408
8434
|
})
|
|
8409
8435
|
});
|
|
8410
8436
|
}
|
|
@@ -11451,12 +11477,12 @@ function createSystemCollectorClock() {
|
|
|
11451
11477
|
return {
|
|
11452
11478
|
wallNow: () => /* @__PURE__ */ new Date(),
|
|
11453
11479
|
monoNow: () => Number(process.hrtime.bigint() - start) / 1e6,
|
|
11454
|
-
sleep: (ms, signal) => new Promise((
|
|
11480
|
+
sleep: (ms, signal) => new Promise((resolve20, reject) => {
|
|
11455
11481
|
if (signal?.aborted) {
|
|
11456
11482
|
reject(signal.reason ?? new Error("aborted"));
|
|
11457
11483
|
return;
|
|
11458
11484
|
}
|
|
11459
|
-
const timer = setTimeout(
|
|
11485
|
+
const timer = setTimeout(resolve20, ms);
|
|
11460
11486
|
const onAbort = () => {
|
|
11461
11487
|
clearTimeout(timer);
|
|
11462
11488
|
reject(signal?.reason ?? new Error("aborted"));
|
|
@@ -14877,10 +14903,10 @@ function presentFailureTerminal(terminal, io) {
|
|
|
14877
14903
|
}
|
|
14878
14904
|
function defaultNavigatorGraceSleep() {
|
|
14879
14905
|
let timer;
|
|
14880
|
-
const sleep = ((ms) => new Promise((
|
|
14906
|
+
const sleep = ((ms) => new Promise((resolve20) => {
|
|
14881
14907
|
timer = setTimeout(() => {
|
|
14882
14908
|
timer = void 0;
|
|
14883
|
-
|
|
14909
|
+
resolve20();
|
|
14884
14910
|
}, ms);
|
|
14885
14911
|
}));
|
|
14886
14912
|
sleep.cancel = () => {
|
|
@@ -14892,7 +14918,7 @@ function defaultNavigatorGraceSleep() {
|
|
|
14892
14918
|
return sleep;
|
|
14893
14919
|
}
|
|
14894
14920
|
function raceNavigatorGrace(work, graceMs = NAVIGATOR_POST_ROLE_GRACE_MS, sleep = defaultNavigatorGraceSleep()) {
|
|
14895
|
-
return new Promise((
|
|
14921
|
+
return new Promise((resolve20, reject) => {
|
|
14896
14922
|
let settled = false;
|
|
14897
14923
|
const finish = (action) => {
|
|
14898
14924
|
if (settled) return;
|
|
@@ -14901,11 +14927,11 @@ function raceNavigatorGrace(work, graceMs = NAVIGATOR_POST_ROLE_GRACE_MS, sleep
|
|
|
14901
14927
|
action();
|
|
14902
14928
|
};
|
|
14903
14929
|
void work.then(
|
|
14904
|
-
(value) => finish(() =>
|
|
14930
|
+
(value) => finish(() => resolve20({ status: "done", value })),
|
|
14905
14931
|
(error) => finish(() => reject(error))
|
|
14906
14932
|
);
|
|
14907
14933
|
void sleep(graceMs).then(() => {
|
|
14908
|
-
finish(() =>
|
|
14934
|
+
finish(() => resolve20({ status: "timeout" }));
|
|
14909
14935
|
});
|
|
14910
14936
|
});
|
|
14911
14937
|
}
|
|
@@ -24155,7 +24181,7 @@ import { mkdtemp as mkdtemp3, rm as rm3 } from "node:fs/promises";
|
|
|
24155
24181
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
24156
24182
|
import { join as join32 } from "node:path";
|
|
24157
24183
|
async function runCommand(command, args, options = {}) {
|
|
24158
|
-
return await new Promise((
|
|
24184
|
+
return await new Promise((resolve20, reject) => {
|
|
24159
24185
|
const child = spawn4(command, args, { ...options.cwd === void 0 ? {} : { cwd: options.cwd }, stdio: ["ignore", "pipe", "pipe"], signal: options.signal });
|
|
24160
24186
|
let stdout = "", stderr = "";
|
|
24161
24187
|
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
@@ -24175,7 +24201,7 @@ async function runCommand(command, args, options = {}) {
|
|
|
24175
24201
|
const actual = code ?? 1;
|
|
24176
24202
|
if ((options.allowedCodes ?? [0]).includes(actual)) {
|
|
24177
24203
|
settled = true;
|
|
24178
|
-
|
|
24204
|
+
resolve20({ stdout, stderr, code: actual });
|
|
24179
24205
|
} else fail5(void 0, code, signal);
|
|
24180
24206
|
});
|
|
24181
24207
|
});
|
|
@@ -24639,16 +24665,21 @@ import { join as join33 } from "node:path";
|
|
|
24639
24665
|
function resolveAcpBinary(description, operatorHome) {
|
|
24640
24666
|
return join33(operatorHome, ...description.binaryFromHome);
|
|
24641
24667
|
}
|
|
24642
|
-
function acpStdioArgs(description, model) {
|
|
24668
|
+
function acpStdioArgs(description, model, seat) {
|
|
24643
24669
|
const { prefix, suffix, modelFlag, thinkingFlag } = description.argv;
|
|
24644
24670
|
const pair = (flag, value) => flag === void 0 || value === void 0 ? [] : [flag, value];
|
|
24645
24671
|
return [
|
|
24672
|
+
...pair(description.seatProfileSoul?.flag, seat?.profileName),
|
|
24673
|
+
...pair(thinkingFlag, model?.thinking),
|
|
24646
24674
|
...prefix,
|
|
24647
24675
|
...pair(modelFlag, model?.model),
|
|
24648
|
-
...pair(thinkingFlag, model?.thinking),
|
|
24649
24676
|
...suffix
|
|
24650
24677
|
];
|
|
24651
24678
|
}
|
|
24679
|
+
function acpModelId(modelPassing, model) {
|
|
24680
|
+
if (model?.model === void 0) return void 0;
|
|
24681
|
+
return modelPassing === "set_model" ? `${model.provider}:${model.model}` : model.model;
|
|
24682
|
+
}
|
|
24652
24683
|
|
|
24653
24684
|
// src/acp-host/role-envelope.ts
|
|
24654
24685
|
init_gatekeeper_pass_envelope();
|
|
@@ -24750,8 +24781,8 @@ function connectAcpStdio(options) {
|
|
|
24750
24781
|
request(method, params) {
|
|
24751
24782
|
if (closed) return Promise.reject(terminalError ?? acpError("acp-connection-closed", "ACP connection is closed"));
|
|
24752
24783
|
const id = ++nextId;
|
|
24753
|
-
return new Promise((
|
|
24754
|
-
pending.set(id, { resolve:
|
|
24784
|
+
return new Promise((resolve20, reject) => {
|
|
24785
|
+
pending.set(id, { resolve: resolve20, reject });
|
|
24755
24786
|
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
|
|
24756
24787
|
`, (error) => {
|
|
24757
24788
|
if (error === null || error === void 0) return;
|
|
@@ -24775,7 +24806,7 @@ function connectAcpStdio(options) {
|
|
|
24775
24806
|
settleClosed(acpError("acp-connection-closed", "ACP connection is closed"));
|
|
24776
24807
|
child.stdin.end();
|
|
24777
24808
|
child.kill("SIGTERM");
|
|
24778
|
-
await new Promise((
|
|
24809
|
+
await new Promise((resolve20) => child.once("close", () => resolve20()));
|
|
24779
24810
|
}
|
|
24780
24811
|
});
|
|
24781
24812
|
}
|
|
@@ -24786,6 +24817,7 @@ function createAcpRoleTurnHost(config) {
|
|
|
24786
24817
|
const execution = serial.then(async () => {
|
|
24787
24818
|
const continuation = request.continuation;
|
|
24788
24819
|
const prepared = await config.prepare(request);
|
|
24820
|
+
const systemPromptOverride = renderAcpSystemPromptOverride(prepared.systemPrompt);
|
|
24789
24821
|
let connection;
|
|
24790
24822
|
let sessionId;
|
|
24791
24823
|
let accepted = false;
|
|
@@ -24801,40 +24833,47 @@ function createAcpRoleTurnHost(config) {
|
|
|
24801
24833
|
const initializeMeta = initialized._meta;
|
|
24802
24834
|
const modelState = initializeMeta?.modelState;
|
|
24803
24835
|
const availableModels = Array.isArray(modelState?.availableModels) ? modelState.availableModels : void 0;
|
|
24804
|
-
if (request.model !== void 0 && availableModels !== void 0 && !availableModels.some((entry) => typeof entry === "object" && entry !== null && entry.modelId === request.model
|
|
24836
|
+
if (request.model !== void 0 && availableModels !== void 0 && !availableModels.some((entry) => typeof entry === "object" && entry !== null && entry.modelId === acpModelId(config.modelPassing, request.model))) {
|
|
24805
24837
|
return failure("activation", "AcpHostModelMismatch", "host-model-mismatch", {
|
|
24806
24838
|
provider: request.model.provider,
|
|
24807
24839
|
model: request.model.model
|
|
24808
24840
|
});
|
|
24809
24841
|
}
|
|
24810
24842
|
const priorNativePaths = continuation.kind === "resume" ? request.hostTransition?.priorNativePaths : void 0;
|
|
24843
|
+
const loadConnection = connection;
|
|
24844
|
+
const sessionBindParams = {
|
|
24845
|
+
cwd: request.cwd,
|
|
24846
|
+
mcpServers: prepared.mcpServers,
|
|
24847
|
+
_meta: { systemPromptOverride, yoloMode: false }
|
|
24848
|
+
};
|
|
24849
|
+
const loadSession = async (bindSessionId) => {
|
|
24850
|
+
const loaded = await loadConnection.request("session/load", {
|
|
24851
|
+
sessionId: bindSessionId,
|
|
24852
|
+
...sessionBindParams
|
|
24853
|
+
});
|
|
24854
|
+
return typeof loaded.sessionId === "string" && loaded.sessionId !== "" ? loaded.sessionId : bindSessionId;
|
|
24855
|
+
};
|
|
24811
24856
|
if (continuation.kind === "resume" && config.boundResume === "session/load") {
|
|
24812
24857
|
const boundSessionId = await config.sessionIdentity.load(request.principal);
|
|
24813
24858
|
if (boundSessionId !== void 0 && boundSessionId !== "") {
|
|
24814
|
-
|
|
24815
|
-
sessionId: boundSessionId,
|
|
24816
|
-
cwd: request.cwd,
|
|
24817
|
-
mcpServers: prepared.mcpServers,
|
|
24818
|
-
_meta: { systemPromptOverride: renderAcpSystemPromptOverride(prepared.systemPrompt), yoloMode: false }
|
|
24819
|
-
});
|
|
24820
|
-
sessionId = typeof loaded.sessionId === "string" && loaded.sessionId !== "" ? loaded.sessionId : boundSessionId;
|
|
24859
|
+
sessionId = await loadSession(boundSessionId);
|
|
24821
24860
|
}
|
|
24822
24861
|
}
|
|
24823
24862
|
if (sessionId === void 0) {
|
|
24824
|
-
const session = await connection.request(
|
|
24825
|
-
"session/new",
|
|
24826
|
-
{
|
|
24827
|
-
cwd: request.cwd,
|
|
24828
|
-
mcpServers: prepared.mcpServers,
|
|
24829
|
-
_meta: { systemPromptOverride: renderAcpSystemPromptOverride(prepared.systemPrompt), yoloMode: false }
|
|
24830
|
-
}
|
|
24831
|
-
);
|
|
24863
|
+
const session = await connection.request("session/new", sessionBindParams);
|
|
24832
24864
|
sessionId = typeof session.sessionId === "string" ? session.sessionId : void 0;
|
|
24833
24865
|
if (sessionId === void 0 || sessionId === "") {
|
|
24834
24866
|
return failure("session", "AcpSessionFailure", "session-id-missing");
|
|
24835
24867
|
}
|
|
24836
24868
|
await config.sessionIdentity.bind(request.principal, sessionId);
|
|
24837
24869
|
}
|
|
24870
|
+
if (config.modelPassing === "set_model" && request.model !== void 0 && sessionId !== void 0) {
|
|
24871
|
+
await connection.request("session/set_model", {
|
|
24872
|
+
sessionId,
|
|
24873
|
+
modelId: acpModelId(config.modelPassing, request.model)
|
|
24874
|
+
});
|
|
24875
|
+
sessionId = await loadSession(sessionId);
|
|
24876
|
+
}
|
|
24838
24877
|
let prompt = priorNativePaths !== void 0 && priorNativePaths.length > 0 ? `${prepared.prompt}
|
|
24839
24878
|
${priorNativePaths.join("\n")}` : prepared.prompt;
|
|
24840
24879
|
const abortSignal = request.signal === void 0 ? prepared.abortSignal : prepared.abortSignal === void 0 ? request.signal : AbortSignal.any([prepared.abortSignal, request.signal]);
|
|
@@ -24845,7 +24884,7 @@ ${priorNativePaths.join("\n")}` : prepared.prompt;
|
|
|
24845
24884
|
}
|
|
24846
24885
|
const promptRequest = activeConnection.request("session/prompt", params);
|
|
24847
24886
|
if (abortSignal === void 0) return promptRequest;
|
|
24848
|
-
return new Promise((
|
|
24887
|
+
return new Promise((resolve20, reject) => {
|
|
24849
24888
|
let settled = false;
|
|
24850
24889
|
const onAbort = () => {
|
|
24851
24890
|
if (settled) return;
|
|
@@ -24860,7 +24899,7 @@ ${priorNativePaths.join("\n")}` : prepared.prompt;
|
|
|
24860
24899
|
if (settled) return;
|
|
24861
24900
|
settled = true;
|
|
24862
24901
|
abortSignal.removeEventListener("abort", onAbort);
|
|
24863
|
-
|
|
24902
|
+
resolve20(value);
|
|
24864
24903
|
},
|
|
24865
24904
|
(error) => {
|
|
24866
24905
|
if (settled) return;
|
|
@@ -24874,12 +24913,9 @@ ${priorNativePaths.join("\n")}` : prepared.prompt;
|
|
|
24874
24913
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
24875
24914
|
let result;
|
|
24876
24915
|
try {
|
|
24877
|
-
const promptParts = [
|
|
24878
|
-
{ type: "text", text: prompt }
|
|
24879
|
-
];
|
|
24880
24916
|
result = await promptOrAbort({
|
|
24881
24917
|
sessionId,
|
|
24882
|
-
prompt:
|
|
24918
|
+
prompt: [{ type: "text", text: prompt }]
|
|
24883
24919
|
});
|
|
24884
24920
|
} catch (error) {
|
|
24885
24921
|
if (typeof error === "object" && error !== null && error.code === "host-aborted") {
|
|
@@ -24984,11 +25020,11 @@ function projectAcpActivationFlags(request) {
|
|
|
24984
25020
|
return flags;
|
|
24985
25021
|
}
|
|
24986
25022
|
async function listen(server, path) {
|
|
24987
|
-
await new Promise((
|
|
25023
|
+
await new Promise((resolve20, reject) => {
|
|
24988
25024
|
server.once("error", reject);
|
|
24989
25025
|
server.listen(path, () => {
|
|
24990
25026
|
server.off("error", reject);
|
|
24991
|
-
|
|
25027
|
+
resolve20();
|
|
24992
25028
|
});
|
|
24993
25029
|
});
|
|
24994
25030
|
}
|
|
@@ -25379,8 +25415,8 @@ async function prepareAcpRoleEnvelope(options) {
|
|
|
25379
25415
|
try {
|
|
25380
25416
|
const closeAll = server.closeAllConnections;
|
|
25381
25417
|
if (typeof closeAll === "function") closeAll.call(server);
|
|
25382
|
-
await new Promise((
|
|
25383
|
-
server.close((error) => error ? reject(error) :
|
|
25418
|
+
await new Promise((resolve20, reject) => {
|
|
25419
|
+
server.close((error) => error ? reject(error) : resolve20());
|
|
25384
25420
|
});
|
|
25385
25421
|
} catch (error) {
|
|
25386
25422
|
cleanupFailures.push(error);
|
|
@@ -25488,11 +25524,70 @@ async function prepareAcpRoleEnvelope(options) {
|
|
|
25488
25524
|
}
|
|
25489
25525
|
}
|
|
25490
25526
|
|
|
25527
|
+
// src/acp-host/seat-profile-soul.ts
|
|
25528
|
+
import { constants as constants3 } from "node:fs";
|
|
25529
|
+
import { access as access4, copyFile, lstat as lstat6, mkdir as mkdir6, readlink, symlink, unlink as unlink4 } from "node:fs/promises";
|
|
25530
|
+
import { dirname as dirname18, join as join35, relative as relative3, resolve as resolve19 } from "node:path";
|
|
25531
|
+
function seatProfileName(spec, role) {
|
|
25532
|
+
return `${spec.namePrefix}${role}`;
|
|
25533
|
+
}
|
|
25534
|
+
function packageRoleSoulPath(packageRoot, role) {
|
|
25535
|
+
return join35(packageRoot, "souls", `${role}.md`);
|
|
25536
|
+
}
|
|
25537
|
+
async function pathExists(path) {
|
|
25538
|
+
try {
|
|
25539
|
+
await access4(path, constants3.F_OK);
|
|
25540
|
+
return true;
|
|
25541
|
+
} catch {
|
|
25542
|
+
return false;
|
|
25543
|
+
}
|
|
25544
|
+
}
|
|
25545
|
+
async function ensureSeatProfileSoul(options) {
|
|
25546
|
+
const { spec, operatorHome, packageRoot, role } = options;
|
|
25547
|
+
const profileName = seatProfileName(spec, role);
|
|
25548
|
+
const soulTarget = resolve19(packageRoleSoulPath(packageRoot, role));
|
|
25549
|
+
if (!await pathExists(soulTarget)) {
|
|
25550
|
+
throw new Error(`packaged role soul missing: ${soulTarget}`);
|
|
25551
|
+
}
|
|
25552
|
+
const profilesRoot = join35(operatorHome, ...spec.profilesRootFromHome);
|
|
25553
|
+
const profileDir = join35(profilesRoot, profileName);
|
|
25554
|
+
const hostRoot = dirname18(profilesRoot);
|
|
25555
|
+
const soulPath = join35(profileDir, spec.soulFileName);
|
|
25556
|
+
if (!await pathExists(profileDir)) {
|
|
25557
|
+
await mkdir6(profileDir, { recursive: true });
|
|
25558
|
+
for (const name of ["auth.json", ".env", "config.yaml"]) {
|
|
25559
|
+
const source = join35(hostRoot, name);
|
|
25560
|
+
if (!await pathExists(source)) continue;
|
|
25561
|
+
await copyFile(source, join35(profileDir, name));
|
|
25562
|
+
}
|
|
25563
|
+
} else {
|
|
25564
|
+
await mkdir6(profileDir, { recursive: true });
|
|
25565
|
+
}
|
|
25566
|
+
const desiredLink = relative3(profileDir, soulTarget);
|
|
25567
|
+
let current;
|
|
25568
|
+
try {
|
|
25569
|
+
const st = await lstat6(soulPath);
|
|
25570
|
+
if (st.isSymbolicLink()) {
|
|
25571
|
+
current = await readlink(soulPath);
|
|
25572
|
+
}
|
|
25573
|
+
} catch {
|
|
25574
|
+
current = void 0;
|
|
25575
|
+
}
|
|
25576
|
+
if (current === desiredLink || current === soulTarget) {
|
|
25577
|
+
return profileName;
|
|
25578
|
+
}
|
|
25579
|
+
if (await pathExists(soulPath) || current !== void 0) {
|
|
25580
|
+
await unlink4(soulPath);
|
|
25581
|
+
}
|
|
25582
|
+
await symlink(desiredLink, soulPath);
|
|
25583
|
+
return profileName;
|
|
25584
|
+
}
|
|
25585
|
+
|
|
25491
25586
|
// src/acp-host/session-identity.ts
|
|
25492
|
-
import { mkdir as
|
|
25493
|
-
import { dirname as
|
|
25587
|
+
import { mkdir as mkdir7, readFile as readFile19, rename, writeFile as writeFile9 } from "node:fs/promises";
|
|
25588
|
+
import { dirname as dirname19, join as join36 } from "node:path";
|
|
25494
25589
|
function createAcpSessionIdentityAuthority(authority, sessionBindingFile) {
|
|
25495
|
-
const bindingPath = (principal) =>
|
|
25590
|
+
const bindingPath = (principal) => join36(authority.decode(principal).sessionDirectory, sessionBindingFile);
|
|
25496
25591
|
return {
|
|
25497
25592
|
resolveSessionFile(principal) {
|
|
25498
25593
|
return authority.decode(principal).sessionFile;
|
|
@@ -25511,7 +25606,7 @@ function createAcpSessionIdentityAuthority(authority, sessionBindingFile) {
|
|
|
25511
25606
|
},
|
|
25512
25607
|
async bind(principal, sessionId) {
|
|
25513
25608
|
const target = bindingPath(principal);
|
|
25514
|
-
await
|
|
25609
|
+
await mkdir7(dirname19(target), { recursive: true });
|
|
25515
25610
|
const temporary = `${target}.${process.pid}.tmp`;
|
|
25516
25611
|
await writeFile9(temporary, `${JSON.stringify({ sessionId })}
|
|
25517
25612
|
`, { encoding: "utf8", mode: 384 });
|
|
@@ -25598,11 +25693,23 @@ function createProductionAcpRoleTurnHost(options) {
|
|
|
25598
25693
|
return createComposedAcpRoleTurnHost({
|
|
25599
25694
|
sessionIdentity: createAcpSessionIdentityAuthority(principalAuthority, description.sessionBindingFile),
|
|
25600
25695
|
boundResume: description.boundResume,
|
|
25696
|
+
modelPassing: description.modelPassing,
|
|
25601
25697
|
roleRuntimeDependencies: createAcpRoleRuntimeDependencies(packageRoot),
|
|
25602
25698
|
async connect(request) {
|
|
25699
|
+
const seatProfile = description.seatProfileSoul;
|
|
25700
|
+
const profileName = seatProfile === void 0 ? void 0 : await ensureSeatProfileSoul({
|
|
25701
|
+
spec: seatProfile,
|
|
25702
|
+
operatorHome: request.home,
|
|
25703
|
+
packageRoot,
|
|
25704
|
+
role: request.activation.role
|
|
25705
|
+
});
|
|
25603
25706
|
return connectAcpStdio({
|
|
25604
25707
|
binary: resolveAcpBinary(description, request.home),
|
|
25605
|
-
args: acpStdioArgs(
|
|
25708
|
+
args: acpStdioArgs(
|
|
25709
|
+
description,
|
|
25710
|
+
request.model,
|
|
25711
|
+
profileName === void 0 ? void 0 : { profileName }
|
|
25712
|
+
),
|
|
25606
25713
|
cwd: request.cwd,
|
|
25607
25714
|
env
|
|
25608
25715
|
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seat-scoped host profile soul delivery (#644 / 拍 2).
|
|
3
|
+
*
|
|
4
|
+
* Hermes has no per-session systemPrompt channel; identity is the profile's
|
|
5
|
+
* SOUL.md. Each seat owns `profiles/<namePrefix><role>/`, and SOUL.md is a
|
|
6
|
+
* symlink to the packaged `souls/<role>.md` (package is the sole soul source).
|
|
7
|
+
*/
|
|
8
|
+
import { constants } from "node:fs";
|
|
9
|
+
import { access, copyFile, lstat, mkdir, readlink, symlink, unlink } from "node:fs/promises";
|
|
10
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
11
|
+
/** Profile id for one seat role. */
|
|
12
|
+
export function seatProfileName(spec, role) {
|
|
13
|
+
return `${spec.namePrefix}${role}`;
|
|
14
|
+
}
|
|
15
|
+
/** Packaged soul path for one role (`souls/<role>.md`). */
|
|
16
|
+
export function packageRoleSoulPath(packageRoot, role) {
|
|
17
|
+
return join(packageRoot, "souls", `${role}.md`);
|
|
18
|
+
}
|
|
19
|
+
async function pathExists(path) {
|
|
20
|
+
try {
|
|
21
|
+
await access(path, constants.F_OK);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Ensure the seat profile directory exists and SOUL.md is a symlink to the
|
|
30
|
+
* packaged role soul. On first create, copy credential surfaces from the host
|
|
31
|
+
* root (parent of the profiles root) so the profile can authenticate without
|
|
32
|
+
* touching the default profile in place. Returns the profile id for argv.
|
|
33
|
+
*/
|
|
34
|
+
export async function ensureSeatProfileSoul(options) {
|
|
35
|
+
const { spec, operatorHome, packageRoot, role } = options;
|
|
36
|
+
const profileName = seatProfileName(spec, role);
|
|
37
|
+
const soulTarget = resolve(packageRoleSoulPath(packageRoot, role));
|
|
38
|
+
if (!(await pathExists(soulTarget))) {
|
|
39
|
+
throw new Error(`packaged role soul missing: ${soulTarget}`);
|
|
40
|
+
}
|
|
41
|
+
const profilesRoot = join(operatorHome, ...spec.profilesRootFromHome);
|
|
42
|
+
const profileDir = join(profilesRoot, profileName);
|
|
43
|
+
const hostRoot = dirname(profilesRoot);
|
|
44
|
+
const soulPath = join(profileDir, spec.soulFileName);
|
|
45
|
+
if (!(await pathExists(profileDir))) {
|
|
46
|
+
await mkdir(profileDir, { recursive: true });
|
|
47
|
+
// First-create credential bootstrap only. Never rewrite an existing profile's
|
|
48
|
+
// auth/config; never write into the host root / default profile.
|
|
49
|
+
for (const name of ["auth.json", ".env", "config.yaml"]) {
|
|
50
|
+
const source = join(hostRoot, name);
|
|
51
|
+
if (!(await pathExists(source)))
|
|
52
|
+
continue;
|
|
53
|
+
await copyFile(source, join(profileDir, name));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await mkdir(profileDir, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
const desiredLink = relative(profileDir, soulTarget);
|
|
60
|
+
let current;
|
|
61
|
+
try {
|
|
62
|
+
const st = await lstat(soulPath);
|
|
63
|
+
if (st.isSymbolicLink()) {
|
|
64
|
+
current = await readlink(soulPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
current = undefined;
|
|
69
|
+
}
|
|
70
|
+
if (current === desiredLink || current === soulTarget) {
|
|
71
|
+
return profileName;
|
|
72
|
+
}
|
|
73
|
+
if (await pathExists(soulPath) || current !== undefined) {
|
|
74
|
+
await unlink(soulPath);
|
|
75
|
+
}
|
|
76
|
+
await symlink(desiredLink, soulPath);
|
|
77
|
+
return profileName;
|
|
78
|
+
}
|
|
@@ -10,6 +10,7 @@ export const HOST_DESCRIPTIONS = Object.freeze({
|
|
|
10
10
|
suffix: Object.freeze(["stdio"]),
|
|
11
11
|
modelFlag: "--model",
|
|
12
12
|
}),
|
|
13
|
+
modelPassing: "argv",
|
|
13
14
|
boundResume: "session/load",
|
|
14
15
|
sessionBindingFile: "grok-acp-session.json",
|
|
15
16
|
childEnv: Object.freeze({
|
|
@@ -18,6 +19,31 @@ export const HOST_DESCRIPTIONS = Object.freeze({
|
|
|
18
19
|
GROK_SUBAGENTS: "0",
|
|
19
20
|
}),
|
|
20
21
|
}),
|
|
22
|
+
/**
|
|
23
|
+
* Operator home `~/.hermes`, native session/load resume, `acp` subcommand.
|
|
24
|
+
* Model arrives as an ACP `session/set_model` RPC with modelId `provider:model`
|
|
25
|
+
* (seat table provider + model concatenated). Reasoning is the global
|
|
26
|
+
* `--reasoning` flag before `acp`. Soul is the seat profile SOUL.md symlink
|
|
27
|
+
* (`hermes -p ak-<role> …`); package `souls/<role>.md` is the sole source.
|
|
28
|
+
*/
|
|
29
|
+
"hermes": Object.freeze({
|
|
30
|
+
binaryFromHome: Object.freeze([".local", "bin", "hermes"]),
|
|
31
|
+
argv: Object.freeze({
|
|
32
|
+
prefix: Object.freeze(["acp"]),
|
|
33
|
+
suffix: Object.freeze([]),
|
|
34
|
+
thinkingFlag: "--reasoning",
|
|
35
|
+
}),
|
|
36
|
+
modelPassing: "set_model",
|
|
37
|
+
boundResume: "session/load",
|
|
38
|
+
sessionBindingFile: "hermes-acp-session.json",
|
|
39
|
+
childEnv: Object.freeze({}),
|
|
40
|
+
seatProfileSoul: Object.freeze({
|
|
41
|
+
flag: "-p",
|
|
42
|
+
namePrefix: "ak-",
|
|
43
|
+
profilesRootFromHome: Object.freeze([".hermes", "profiles"]),
|
|
44
|
+
soulFileName: "SOUL.md",
|
|
45
|
+
}),
|
|
46
|
+
}),
|
|
21
47
|
});
|
|
22
48
|
export function lookupHostDescription(host) {
|
|
23
49
|
return Object.hasOwn(HOST_DESCRIPTIONS, host) ? HOST_DESCRIPTIONS[host] : undefined;
|
package/dist/public-cli/main.js
CHANGED
|
@@ -15446,6 +15446,7 @@ var init_host_descriptions = __esm({
|
|
|
15446
15446
|
suffix: Object.freeze(["stdio"]),
|
|
15447
15447
|
modelFlag: "--model"
|
|
15448
15448
|
}),
|
|
15449
|
+
modelPassing: "argv",
|
|
15449
15450
|
boundResume: "session/load",
|
|
15450
15451
|
sessionBindingFile: "grok-acp-session.json",
|
|
15451
15452
|
childEnv: Object.freeze({
|
|
@@ -15453,6 +15454,31 @@ var init_host_descriptions = __esm({
|
|
|
15453
15454
|
GROK_MEMORY: "0",
|
|
15454
15455
|
GROK_SUBAGENTS: "0"
|
|
15455
15456
|
})
|
|
15457
|
+
}),
|
|
15458
|
+
/**
|
|
15459
|
+
* Operator home `~/.hermes`, native session/load resume, `acp` subcommand.
|
|
15460
|
+
* Model arrives as an ACP `session/set_model` RPC with modelId `provider:model`
|
|
15461
|
+
* (seat table provider + model concatenated). Reasoning is the global
|
|
15462
|
+
* `--reasoning` flag before `acp`. Soul is the seat profile SOUL.md symlink
|
|
15463
|
+
* (`hermes -p ak-<role> …`); package `souls/<role>.md` is the sole source.
|
|
15464
|
+
*/
|
|
15465
|
+
"hermes": Object.freeze({
|
|
15466
|
+
binaryFromHome: Object.freeze([".local", "bin", "hermes"]),
|
|
15467
|
+
argv: Object.freeze({
|
|
15468
|
+
prefix: Object.freeze(["acp"]),
|
|
15469
|
+
suffix: Object.freeze([]),
|
|
15470
|
+
thinkingFlag: "--reasoning"
|
|
15471
|
+
}),
|
|
15472
|
+
modelPassing: "set_model",
|
|
15473
|
+
boundResume: "session/load",
|
|
15474
|
+
sessionBindingFile: "hermes-acp-session.json",
|
|
15475
|
+
childEnv: Object.freeze({}),
|
|
15476
|
+
seatProfileSoul: Object.freeze({
|
|
15477
|
+
flag: "-p",
|
|
15478
|
+
namePrefix: "ak-",
|
|
15479
|
+
profilesRootFromHome: Object.freeze([".hermes", "profiles"]),
|
|
15480
|
+
soulFileName: "SOUL.md"
|
|
15481
|
+
})
|
|
15456
15482
|
})
|
|
15457
15483
|
});
|
|
15458
15484
|
}
|
package/package.json
CHANGED
package/souls/countersign.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
2 第二发源立法:
|
|
12
12
|
适用 ADR——由你自行检索,按票面触及的席位、接缝、术语在 docs/adr 与 CONTEXT.md 查全,票面引没引都算——并逐键列出其决策键;质量法、审刑院法典;
|
|
13
13
|
|
|
14
|
-
3 第二法源合法性检查:判断第二法源 以及 输入的
|
|
14
|
+
3 第二法源合法性检查:判断第二法源 以及 输入的 prompt等 对比第一法源是否有超出或不足,曲解。如有,除非有陛下原话批准,否则直接封驳 要求改正或陛下批准。
|
|
15
15
|
|
|
16
16
|
**二、相抵。** 票面,相关票与在飞分支与法源有无抵触。
|
|
17
17
|
|
package/souls/judge.md
CHANGED
|
@@ -1,68 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
同查),然后落判词。判词与驳回须锚到具体法源;无可依法源时不得以「无法可依」
|
|
23
|
-
收敛,应上抛请立法。立案先于按派单收缩的审理范围。
|
|
24
|
-
|
|
25
|
-
**法源位阶:已落定 ADR 的具体决策在本仓优先于宪法通则;疑似违宪须核其所绑陛下原话与 decision key。已批断言不容翻。** 动过既有断言的改动须溯源到票面授权、ADR 或
|
|
26
|
-
先前裁定;权威仍在而相抵触即为 blocking。改动触及治理法文件(含 `docs/adr/`、
|
|
27
|
-
`CONTEXT.md`)而票面未授权时,实质审理并在判词中点名;确属宪法问题则上抛。
|
|
28
|
-
|
|
29
|
-
**全部提交物都是案面**:票面、计划、处置清单、packet、派单措辞与输入 prompt、
|
|
30
|
-
代码、测试。举证责任由所审材料决定,不由派单指定。
|
|
31
|
-
|
|
32
|
-
**审查范围绑定本次授权的 target/range**,覆盖范围内完整变更与未声明的生产
|
|
33
|
-
默认行为;不得因派单或票面清单缩减举证责任。派单非法缩 scope:可施工纠正判
|
|
34
|
-
`continue`;属陛下取舍、法源冲突或无法安全裁定判 `escalate`。
|
|
35
|
-
|
|
36
|
-
**落判前逐项过改动清单,与票面双向对表。** 每项改动标明由票面哪条驱动,
|
|
37
|
-
标不出来的即为夹带;票面有要求而清单没有的即为缺口(夹带的默认值、护栏、
|
|
38
|
-
平行机制、仅测试用的生产钩子同论)。多与少同为缺陷,逐条处置;清单未列全
|
|
39
|
-
不得收敛。
|
|
40
|
-
|
|
41
|
-
## 修理原则
|
|
42
|
-
|
|
43
|
-
删除或简化根因优先于增加平行机制。提出或批准护栏前必答三问:①哪个真实、
|
|
44
|
-
可复现的失败证明需要它?②哪个接缝拥有它要维护的不变式?③为什么删除或简化
|
|
45
|
-
根因不足以解决这类失败?
|
|
46
|
-
|
|
47
|
-
审查修复波及面,区分原始缺陷所需改动、修复引入后再修的改动和无依据的新机制;
|
|
48
|
-
后两类主导时优先删除/简化。超出本次授权的取舍必须上抛。**一轮轮只加不减的
|
|
49
|
-
修复流,本身就是该上报的病。**
|
|
50
|
-
|
|
51
|
-
**测试质量亲审。** 亲审测试是否忠实证明所主张行为并向 spec 负责;准绳唯 quality-law。
|
|
52
|
-
|
|
53
|
-
## 判词
|
|
54
|
-
|
|
55
|
-
合法判词只有三种,含义相对所审材料的举证责任:
|
|
56
|
-
|
|
57
|
-
- `converged` — 适用举证责任下的事项均已明确处置。修内司/将作监的提交,建议有
|
|
58
|
-
本轮御史台无 p2 及以上 finding 为辅证(p 级由你自行判定;被你驳回的不计数)。
|
|
59
|
-
- `continue` — 仍有可施工的问题;给出清晰、可验证的修理正文。
|
|
60
|
-
- `escalate` — 存在必须由陛下决定的门槛、相互冲突的法源,或依据失败走势
|
|
61
|
-
与专业证据已无法形成安全裁定;写明问题和可选项。
|
|
62
|
-
|
|
63
|
-
**送修正文由你执笔——判词即包。** 首轮可薄(finding + 法源锚点 + 边界);有
|
|
64
|
-
病史则综合(病史全列、方向可钉、拆除清单明文)。同缝反复、根因不明、范围
|
|
65
|
-
持续发散、疑跨接缝或 flake 即为发散,送修单中要求先做最小化、证据驱动的
|
|
66
|
-
诊断定根因。
|
|
67
|
-
|
|
68
|
-
送修仅含尚未结清、须派施工的根因类;已结清项、过程账与双向抽样不得入送修。
|
|
1
|
+
Judge Soul(大理寺)
|
|
2
|
+
你是大理寺。负责逐条裁决并判断是否收敛;不施工做修复。票庭归给事中
|
|
3
|
+
审理
|
|
4
|
+
一、立第一法源。 起居录、全局宪法,仓级治理文件,
|
|
5
|
+
二、立第二法源。自行查全相关 ADR(票面引了也不等于适用,自行判断。)、CONTEXT.md、票面、派单、输入的prompt等决策相关,你自身自带的质量/测试/审核等法典。
|
|
6
|
+
三、核第二法源:如有违反第一法源且没有陛下原话授权的,直接上呈。
|
|
7
|
+
四、核案。 全审本次授权的 target/range。计划、处置清单、packet、代码、测试都是案面,举证责任由材料决定,不由派单指定。
|
|
8
|
+
每项改动须有票面依据,每项要求须有落实或明确处置;相抵、夹带、缺口一并查(标不出来的即为夹带;有要求而清单没有的即为缺口(夹带的默认值、护栏、平行机制、仅测试用的生产钩子同论)),未授权改治理法文件,未声明的生产默认行为同论。清单不全不得收敛。复审亦全审授权范围,上轮问题不划定本轮案面。
|
|
9
|
+
五、裁断。 主张不是证据。一切 LLM 主张须亲验;当前事实只认当前 head,修复者自述不能替代新 head 上的机械验证与独立复查。出自机器执行,而非任何 LLM 之口的确定性事实,核指针加抽查即可。
|
|
10
|
+
事实与处方分判:事实成立而处方违法源,保留事实,驳回处方或另开合法处方。 每条问题显式记录和处置,严重度由你裁定,不因级别低而遗漏或暗降;裁决与驳回须锚定法源和证据。依当前一手证据独立双向抽样重推,误删与该删未删同查。
|
|
11
|
+
测试质量须亲审。取证与同类复核依《审法》;复杂度、测试质量与成本依《质量法》,不另立一套标准。
|
|
12
|
+
删简根因优先。提出或批准护栏,须说明真实可复现的失败、不变式所属接缝,以及为何删简根因不足。
|
|
13
|
+
区分原缺陷所需改动、修复自身造成的返工、无依据的新机制;后两类主导时优先拆除或简化。同缝反复、根因不明、范围发散、疑跨接缝或 flake,先做最小化、证据驱动的诊断,不继续猜修。一轮轮只加不减,本身就是须上报的问题。
|
|
14
|
+
判词
|
|
15
|
+
|
|
16
|
+
* converged——署:适用举证责任下的事项均已明确处置。施工提交推荐有本轮御史台无 p2 及以上 finding 为辅证,严重度由你裁定,已驳回的不计。
|
|
17
|
+
* continue——封驳:仍有授权内可纠正的问题,给出需修理的问题类别与法源,具体发现可作为案例/证据,不作施工白名单;授权范围内同类问题一并处置,超出授权的取舍上呈。一次扫净所有类别。
|
|
18
|
+
* escalate——上呈:缺少法源、法源冲突、取舍超出授权,或依据失败走势与证据已无法安全裁定;写明争点与选项。
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
封驳时 有病史须列全相关失败经过,钉明方向。送修只含未结清、须施工的根因类,不带已结清项、过程账和双向抽样记录。
|
package/souls/notary.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* One ACP host description. Every host-specific value the generic ACP adapter
|
|
3
|
-
* needs — binary location, argv shape, resume verb, binding filename, child env
|
|
4
|
-
* — is data here; the lifecycle in role-turn-host.ts
|
|
3
|
+
* needs — binary location, argv shape, resume verb, binding filename, child env,
|
|
4
|
+
* optional seat-profile soul — is data here; the lifecycle in role-turn-host.ts
|
|
5
|
+
* stays one copy (#732).
|
|
5
6
|
*/
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
|
|
9
|
+
import type { SeatProfileSoul } from "./seat-profile-soul.ts";
|
|
10
|
+
|
|
8
11
|
export type AcpHostDescription = Readonly<{
|
|
9
12
|
/** Binary path segments relative to the operator home. */
|
|
10
13
|
binaryFromHome: readonly string[];
|
|
@@ -12,13 +15,28 @@ export type AcpHostDescription = Readonly<{
|
|
|
12
15
|
prefix: readonly string[];
|
|
13
16
|
suffix: readonly string[];
|
|
14
17
|
modelFlag?: string;
|
|
18
|
+
/** CLI flag whose value is the seat thinking level; placed before `prefix`
|
|
19
|
+
* so it lands ahead of the subcommand (hermes global `--reasoning`). */
|
|
15
20
|
thinkingFlag?: string;
|
|
16
21
|
}>;
|
|
22
|
+
/**
|
|
23
|
+
* How the seat model reaches the agent:
|
|
24
|
+
* - "argv": passed as the CLI `--model` flag (grok);
|
|
25
|
+
* - "set_model": sent as an ACP `session/set_model` RPC with modelId
|
|
26
|
+
* `provider:model` (hermes).
|
|
27
|
+
*/
|
|
28
|
+
modelPassing: "argv" | "set_model";
|
|
17
29
|
/** Which verb a bound resume uses; "session/new" hosts always mint + bind. */
|
|
18
30
|
boundResume: "session/load" | "session/new";
|
|
19
31
|
/** Durable ACP binding filename written beside the session principal. */
|
|
20
32
|
sessionBindingFile: string;
|
|
21
33
|
childEnv: Readonly<Record<string, string>>;
|
|
34
|
+
/**
|
|
35
|
+
* When set, the production factory ensures a seat profile whose SOUL.md is a
|
|
36
|
+
* symlink to the packaged role soul, and prefixes argv with `flag <name>`.
|
|
37
|
+
* Used by hosts that have no per-session systemPrompt channel (hermes).
|
|
38
|
+
*/
|
|
39
|
+
seatProfileSoul?: SeatProfileSoul;
|
|
22
40
|
}>;
|
|
23
41
|
|
|
24
42
|
/** Absolute agent binary for one operator home. */
|
|
@@ -26,18 +44,35 @@ export function resolveAcpBinary(description: AcpHostDescription, operatorHome:
|
|
|
26
44
|
return join(operatorHome, ...description.binaryFromHome);
|
|
27
45
|
}
|
|
28
46
|
|
|
29
|
-
/** Stdio argv:
|
|
47
|
+
/** Stdio argv: optional profile flag, thinking flag (before the subcommand),
|
|
48
|
+
* prefix, optional model flag pair, suffix. */
|
|
30
49
|
export function acpStdioArgs(
|
|
31
50
|
description: AcpHostDescription,
|
|
32
51
|
model?: { readonly model?: string; readonly thinking?: string },
|
|
52
|
+
seat?: { readonly profileName?: string },
|
|
33
53
|
): string[] {
|
|
34
54
|
const { prefix, suffix, modelFlag, thinkingFlag } = description.argv;
|
|
35
55
|
const pair = (flag: string | undefined, value: string | undefined): string[] =>
|
|
36
56
|
flag === undefined || value === undefined ? [] : [flag, value];
|
|
37
57
|
return [
|
|
58
|
+
...pair(description.seatProfileSoul?.flag, seat?.profileName),
|
|
59
|
+
...pair(thinkingFlag, model?.thinking),
|
|
38
60
|
...prefix,
|
|
39
61
|
...pair(modelFlag, model?.model),
|
|
40
|
-
...pair(thinkingFlag, model?.thinking),
|
|
41
62
|
...suffix,
|
|
42
63
|
];
|
|
43
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The modelId the host addresses the seat model by.
|
|
68
|
+
* "argv" hosts address by bare model name; "set_model" hosts address by the
|
|
69
|
+
* `provider:model` modelId the ACP catalog exposes (seat provider after host
|
|
70
|
+
* alias projection, concatenated — never a package provider map).
|
|
71
|
+
*/
|
|
72
|
+
export function acpModelId(
|
|
73
|
+
modelPassing: AcpHostDescription["modelPassing"],
|
|
74
|
+
model?: { readonly model?: string; readonly provider?: string },
|
|
75
|
+
): string | undefined {
|
|
76
|
+
if (model?.model === undefined) return undefined;
|
|
77
|
+
return modelPassing === "set_model" ? `${model.provider}:${model.model}` : model.model;
|
|
78
|
+
}
|
|
@@ -27,6 +27,7 @@ import { loadGatekeeperSessionMaterials, loadMainRoleSessionMaterials } from "..
|
|
|
27
27
|
import { acpStdioArgs, resolveAcpBinary, type AcpHostDescription } from "./description.ts";
|
|
28
28
|
import { createComposedAcpRoleTurnHost } from "./role-envelope.ts";
|
|
29
29
|
import { connectAcpStdio } from "./role-turn-host.ts";
|
|
30
|
+
import { ensureSeatProfileSoul } from "./seat-profile-soul.ts";
|
|
30
31
|
import { createAcpSessionIdentityAuthority } from "./session-identity.ts";
|
|
31
32
|
|
|
32
33
|
export type ProductionAcpHostOptions = Readonly<{
|
|
@@ -121,11 +122,25 @@ export function createProductionAcpRoleTurnHost(options: ProductionAcpHostOption
|
|
|
121
122
|
return createComposedAcpRoleTurnHost({
|
|
122
123
|
sessionIdentity: createAcpSessionIdentityAuthority(principalAuthority, description.sessionBindingFile),
|
|
123
124
|
boundResume: description.boundResume,
|
|
125
|
+
modelPassing: description.modelPassing,
|
|
124
126
|
roleRuntimeDependencies: createAcpRoleRuntimeDependencies(packageRoot),
|
|
125
127
|
async connect(request) {
|
|
128
|
+
const seatProfile = description.seatProfileSoul;
|
|
129
|
+
const profileName = seatProfile === undefined
|
|
130
|
+
? undefined
|
|
131
|
+
: await ensureSeatProfileSoul({
|
|
132
|
+
spec: seatProfile,
|
|
133
|
+
operatorHome: request.home,
|
|
134
|
+
packageRoot,
|
|
135
|
+
role: request.activation.role,
|
|
136
|
+
});
|
|
126
137
|
return connectAcpStdio({
|
|
127
138
|
binary: resolveAcpBinary(description, request.home),
|
|
128
|
-
args: acpStdioArgs(
|
|
139
|
+
args: acpStdioArgs(
|
|
140
|
+
description,
|
|
141
|
+
request.model,
|
|
142
|
+
profileName === undefined ? undefined : { profileName },
|
|
143
|
+
),
|
|
129
144
|
cwd: request.cwd,
|
|
130
145
|
env,
|
|
131
146
|
});
|
|
@@ -622,7 +622,8 @@ export async function prepareAcpRoleEnvelope(options: {
|
|
|
622
622
|
};
|
|
623
623
|
|
|
624
624
|
// Shared envelope activation. systemPrompt must be ready before session/new
|
|
625
|
-
// (
|
|
625
|
+
// (delivered via _meta.systemPromptOverride where the host honors it), so
|
|
626
|
+
// activation runs during prepare.
|
|
626
627
|
try {
|
|
627
628
|
await emit("session_start", { reason: request.continuation.kind });
|
|
628
629
|
const inputResults = await emit("input", { text: request.continuation.prompt, source: "interactive" });
|
|
@@ -3,7 +3,7 @@ import { createInterface } from "node:readline";
|
|
|
3
3
|
|
|
4
4
|
import type { RoleTurnHost, RoleTurnKnownFailure, RoleTurnRequest, RoleTurnResult } from "../host-contracts.ts";
|
|
5
5
|
import { renderAgentStartMaterials } from "../agent-start-materials.ts";
|
|
6
|
-
import type
|
|
6
|
+
import { acpModelId, type AcpHostDescription } from "./description.ts";
|
|
7
7
|
|
|
8
8
|
/** ACP v1 surface used by the generic ACP adapter. Protocol details stay in this module. */
|
|
9
9
|
export interface AcpConnection {
|
|
@@ -64,6 +64,12 @@ export type AcpRoleTurnHostConfig = Readonly<{
|
|
|
64
64
|
sessionIdentity: AcpSessionIdentityAuthority;
|
|
65
65
|
/** Whether a bound resume reuses the native session or mints a fresh one. */
|
|
66
66
|
boundResume: AcpHostDescription["boundResume"];
|
|
67
|
+
/**
|
|
68
|
+
* How the seat model reaches the agent: "set_model" sends an ACP
|
|
69
|
+
* `session/set_model` RPC with modelId `provider:model` once the session
|
|
70
|
+
* exists (new or loaded); "argv" leaves it to the connect argv (--model).
|
|
71
|
+
*/
|
|
72
|
+
modelPassing: AcpHostDescription["modelPassing"];
|
|
67
73
|
connect(request: RoleTurnRequest): Promise<AcpConnection>;
|
|
68
74
|
prepare(request: RoleTurnRequest): Promise<AcpPreparedTurn>;
|
|
69
75
|
}>;
|
|
@@ -201,6 +207,7 @@ export function createAcpRoleTurnHost(config: AcpRoleTurnHostConfig): RoleTurnHo
|
|
|
201
207
|
const execution = serial.then(async (): Promise<RoleTurnResult> => {
|
|
202
208
|
const continuation = request.continuation;
|
|
203
209
|
const prepared = await config.prepare(request);
|
|
210
|
+
const systemPromptOverride = renderAcpSystemPromptOverride(prepared.systemPrompt);
|
|
204
211
|
let connection: AcpConnection | undefined;
|
|
205
212
|
let sessionId: string | undefined;
|
|
206
213
|
let accepted = false;
|
|
@@ -220,7 +227,8 @@ export function createAcpRoleTurnHost(config: AcpRoleTurnHostConfig): RoleTurnHo
|
|
|
220
227
|
const modelState = initializeMeta?.modelState;
|
|
221
228
|
const availableModels = Array.isArray(modelState?.availableModels) ? modelState.availableModels : undefined;
|
|
222
229
|
if (request.model !== undefined && availableModels !== undefined && !availableModels.some((entry) =>
|
|
223
|
-
typeof entry === "object" && entry !== null
|
|
230
|
+
typeof entry === "object" && entry !== null
|
|
231
|
+
&& (entry as { modelId?: unknown }).modelId === acpModelId(config.modelPassing, request.model))) {
|
|
224
232
|
return failure("activation", "AcpHostModelMismatch", "host-model-mismatch", {
|
|
225
233
|
provider: request.model.provider,
|
|
226
234
|
model: request.model.model,
|
|
@@ -233,32 +241,33 @@ export function createAcpRoleTurnHost(config: AcpRoleTurnHostConfig): RoleTurnHo
|
|
|
233
241
|
continuation.kind === "resume"
|
|
234
242
|
? request.hostTransition?.priorNativePaths
|
|
235
243
|
: undefined;
|
|
244
|
+
const loadConnection = connection;
|
|
245
|
+
// Shared session/new + session/load body (cwd/mcpServers/_meta).
|
|
246
|
+
const sessionBindParams = {
|
|
247
|
+
cwd: request.cwd,
|
|
248
|
+
mcpServers: prepared.mcpServers,
|
|
249
|
+
_meta: { systemPromptOverride, yoloMode: false },
|
|
250
|
+
};
|
|
251
|
+
const loadSession = async (bindSessionId: string): Promise<string> => {
|
|
252
|
+
const loaded = await loadConnection.request("session/load", {
|
|
253
|
+
sessionId: bindSessionId,
|
|
254
|
+
...sessionBindParams,
|
|
255
|
+
});
|
|
256
|
+
return typeof loaded.sessionId === "string" && loaded.sessionId !== ""
|
|
257
|
+
? loaded.sessionId
|
|
258
|
+
: bindSessionId;
|
|
259
|
+
};
|
|
236
260
|
if (continuation.kind === "resume" && config.boundResume === "session/load") {
|
|
237
261
|
// Same-host resume reuses the native ACP session via session/load.
|
|
238
262
|
const boundSessionId = await config.sessionIdentity.load(request.principal);
|
|
239
263
|
if (boundSessionId !== undefined && boundSessionId !== "") {
|
|
240
|
-
|
|
241
|
-
sessionId: boundSessionId,
|
|
242
|
-
cwd: request.cwd,
|
|
243
|
-
mcpServers: prepared.mcpServers,
|
|
244
|
-
_meta: { systemPromptOverride: renderAcpSystemPromptOverride(prepared.systemPrompt), yoloMode: false },
|
|
245
|
-
});
|
|
246
|
-
sessionId = typeof loaded.sessionId === "string" && loaded.sessionId !== ""
|
|
247
|
-
? loaded.sessionId
|
|
248
|
-
: boundSessionId;
|
|
264
|
+
sessionId = await loadSession(boundSessionId);
|
|
249
265
|
}
|
|
250
266
|
}
|
|
251
267
|
if (sessionId === undefined) {
|
|
252
268
|
// Initial run, unbound resume (cross-host / lost binding), or a host
|
|
253
269
|
// whose bound resume is session/new: mint the session and bind it.
|
|
254
|
-
const session = await connection.request(
|
|
255
|
-
"session/new",
|
|
256
|
-
{
|
|
257
|
-
cwd: request.cwd,
|
|
258
|
-
mcpServers: prepared.mcpServers,
|
|
259
|
-
_meta: { systemPromptOverride: renderAcpSystemPromptOverride(prepared.systemPrompt), yoloMode: false },
|
|
260
|
-
},
|
|
261
|
-
);
|
|
270
|
+
const session = await connection.request("session/new", sessionBindParams);
|
|
262
271
|
sessionId = typeof session.sessionId === "string" ? session.sessionId : undefined;
|
|
263
272
|
if (sessionId === undefined || sessionId === "") {
|
|
264
273
|
return failure("session", "AcpSessionFailure", "session-id-missing");
|
|
@@ -266,6 +275,24 @@ export function createAcpRoleTurnHost(config: AcpRoleTurnHostConfig): RoleTurnHo
|
|
|
266
275
|
await config.sessionIdentity.bind(request.principal, sessionId);
|
|
267
276
|
}
|
|
268
277
|
|
|
278
|
+
// set_model hosts address the seat model by `provider:model` once the
|
|
279
|
+
// session exists; argv hosts never reach this RPC. Provider is the seat
|
|
280
|
+
// table value after owner host-alias projection (#778) — concatenated
|
|
281
|
+
// here, never dropped, never remapped in package code. set_model may
|
|
282
|
+
// rebuild the session agent and drop ACP-injected mcpServers, so re-bind
|
|
283
|
+
// via the same loadSession authority (return value is the live session id).
|
|
284
|
+
if (
|
|
285
|
+
config.modelPassing === "set_model"
|
|
286
|
+
&& request.model !== undefined
|
|
287
|
+
&& sessionId !== undefined
|
|
288
|
+
) {
|
|
289
|
+
await connection.request("session/set_model", {
|
|
290
|
+
sessionId,
|
|
291
|
+
modelId: acpModelId(config.modelPassing, request.model),
|
|
292
|
+
});
|
|
293
|
+
sessionId = await loadSession(sessionId);
|
|
294
|
+
}
|
|
295
|
+
|
|
269
296
|
let prompt =
|
|
270
297
|
priorNativePaths !== undefined && priorNativePaths.length > 0
|
|
271
298
|
? `${prepared.prompt}\n${priorNativePaths.join("\n")}`
|
|
@@ -319,12 +346,9 @@ export function createAcpRoleTurnHost(config: AcpRoleTurnHostConfig): RoleTurnHo
|
|
|
319
346
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
320
347
|
let result: Readonly<Record<string, unknown>>;
|
|
321
348
|
try {
|
|
322
|
-
const promptParts: Array<Record<string, unknown>> = [
|
|
323
|
-
{ type: "text", text: prompt },
|
|
324
|
-
];
|
|
325
349
|
result = await promptOrAbort({
|
|
326
350
|
sessionId,
|
|
327
|
-
prompt:
|
|
351
|
+
prompt: [{ type: "text", text: prompt }],
|
|
328
352
|
});
|
|
329
353
|
} catch (error) {
|
|
330
354
|
// Envelope abort (typed infra declaration): closeRound owns the failure record.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seat-scoped host profile soul delivery (#644 / 拍 2).
|
|
3
|
+
*
|
|
4
|
+
* Hermes has no per-session systemPrompt channel; identity is the profile's
|
|
5
|
+
* SOUL.md. Each seat owns `profiles/<namePrefix><role>/`, and SOUL.md is a
|
|
6
|
+
* symlink to the packaged `souls/<role>.md` (package is the sole soul source).
|
|
7
|
+
*/
|
|
8
|
+
import { constants } from "node:fs";
|
|
9
|
+
import { access, copyFile, lstat, mkdir, readlink, symlink, unlink } from "node:fs/promises";
|
|
10
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
11
|
+
|
|
12
|
+
export type SeatProfileSoul = Readonly<{
|
|
13
|
+
/** Argv flag selecting the profile (hermes pre-argparse `-p`). */
|
|
14
|
+
flag: string;
|
|
15
|
+
/** Profile id = `${namePrefix}${role}` (e.g. `ak-judge`). */
|
|
16
|
+
namePrefix: string;
|
|
17
|
+
/** Profile directory root relative to the operator home (`.hermes/profiles`). */
|
|
18
|
+
profilesRootFromHome: readonly string[];
|
|
19
|
+
/** Soul filename inside the profile directory. */
|
|
20
|
+
soulFileName: string;
|
|
21
|
+
}>;
|
|
22
|
+
|
|
23
|
+
/** Profile id for one seat role. */
|
|
24
|
+
export function seatProfileName(spec: SeatProfileSoul, role: string): string {
|
|
25
|
+
return `${spec.namePrefix}${role}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Packaged soul path for one role (`souls/<role>.md`). */
|
|
29
|
+
export function packageRoleSoulPath(packageRoot: string, role: string): string {
|
|
30
|
+
return join(packageRoot, "souls", `${role}.md`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function pathExists(path: string): Promise<boolean> {
|
|
34
|
+
try {
|
|
35
|
+
await access(path, constants.F_OK);
|
|
36
|
+
return true;
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Ensure the seat profile directory exists and SOUL.md is a symlink to the
|
|
44
|
+
* packaged role soul. On first create, copy credential surfaces from the host
|
|
45
|
+
* root (parent of the profiles root) so the profile can authenticate without
|
|
46
|
+
* touching the default profile in place. Returns the profile id for argv.
|
|
47
|
+
*/
|
|
48
|
+
export async function ensureSeatProfileSoul(options: {
|
|
49
|
+
readonly spec: SeatProfileSoul;
|
|
50
|
+
readonly operatorHome: string;
|
|
51
|
+
readonly packageRoot: string;
|
|
52
|
+
readonly role: string;
|
|
53
|
+
}): Promise<string> {
|
|
54
|
+
const { spec, operatorHome, packageRoot, role } = options;
|
|
55
|
+
const profileName = seatProfileName(spec, role);
|
|
56
|
+
const soulTarget = resolve(packageRoleSoulPath(packageRoot, role));
|
|
57
|
+
if (!(await pathExists(soulTarget))) {
|
|
58
|
+
throw new Error(`packaged role soul missing: ${soulTarget}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const profilesRoot = join(operatorHome, ...spec.profilesRootFromHome);
|
|
62
|
+
const profileDir = join(profilesRoot, profileName);
|
|
63
|
+
const hostRoot = dirname(profilesRoot);
|
|
64
|
+
const soulPath = join(profileDir, spec.soulFileName);
|
|
65
|
+
|
|
66
|
+
if (!(await pathExists(profileDir))) {
|
|
67
|
+
await mkdir(profileDir, { recursive: true });
|
|
68
|
+
// First-create credential bootstrap only. Never rewrite an existing profile's
|
|
69
|
+
// auth/config; never write into the host root / default profile.
|
|
70
|
+
for (const name of ["auth.json", ".env", "config.yaml"] as const) {
|
|
71
|
+
const source = join(hostRoot, name);
|
|
72
|
+
if (!(await pathExists(source))) continue;
|
|
73
|
+
await copyFile(source, join(profileDir, name));
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
await mkdir(profileDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const desiredLink = relative(profileDir, soulTarget);
|
|
80
|
+
let current: string | undefined;
|
|
81
|
+
try {
|
|
82
|
+
const st = await lstat(soulPath);
|
|
83
|
+
if (st.isSymbolicLink()) {
|
|
84
|
+
current = await readlink(soulPath);
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
current = undefined;
|
|
88
|
+
}
|
|
89
|
+
if (current === desiredLink || current === soulTarget) {
|
|
90
|
+
return profileName;
|
|
91
|
+
}
|
|
92
|
+
if (await pathExists(soulPath) || current !== undefined) {
|
|
93
|
+
await unlink(soulPath);
|
|
94
|
+
}
|
|
95
|
+
await symlink(desiredLink, soulPath);
|
|
96
|
+
return profileName;
|
|
97
|
+
}
|
package/src/host-descriptions.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const HOST_DESCRIPTIONS: Readonly<Record<string, AcpHostDescription>> = O
|
|
|
24
24
|
suffix: Object.freeze(["stdio"]),
|
|
25
25
|
modelFlag: "--model",
|
|
26
26
|
}),
|
|
27
|
+
modelPassing: "argv",
|
|
27
28
|
boundResume: "session/load",
|
|
28
29
|
sessionBindingFile: "grok-acp-session.json",
|
|
29
30
|
childEnv: Object.freeze({
|
|
@@ -32,6 +33,31 @@ export const HOST_DESCRIPTIONS: Readonly<Record<string, AcpHostDescription>> = O
|
|
|
32
33
|
GROK_SUBAGENTS: "0",
|
|
33
34
|
}),
|
|
34
35
|
}),
|
|
36
|
+
/**
|
|
37
|
+
* Operator home `~/.hermes`, native session/load resume, `acp` subcommand.
|
|
38
|
+
* Model arrives as an ACP `session/set_model` RPC with modelId `provider:model`
|
|
39
|
+
* (seat table provider + model concatenated). Reasoning is the global
|
|
40
|
+
* `--reasoning` flag before `acp`. Soul is the seat profile SOUL.md symlink
|
|
41
|
+
* (`hermes -p ak-<role> …`); package `souls/<role>.md` is the sole source.
|
|
42
|
+
*/
|
|
43
|
+
"hermes": Object.freeze({
|
|
44
|
+
binaryFromHome: Object.freeze([".local", "bin", "hermes"]),
|
|
45
|
+
argv: Object.freeze({
|
|
46
|
+
prefix: Object.freeze(["acp"]),
|
|
47
|
+
suffix: Object.freeze([]),
|
|
48
|
+
thinkingFlag: "--reasoning",
|
|
49
|
+
}),
|
|
50
|
+
modelPassing: "set_model",
|
|
51
|
+
boundResume: "session/load",
|
|
52
|
+
sessionBindingFile: "hermes-acp-session.json",
|
|
53
|
+
childEnv: Object.freeze({}),
|
|
54
|
+
seatProfileSoul: Object.freeze({
|
|
55
|
+
flag: "-p",
|
|
56
|
+
namePrefix: "ak-",
|
|
57
|
+
profilesRootFromHome: Object.freeze([".hermes", "profiles"]),
|
|
58
|
+
soulFileName: "SOUL.md",
|
|
59
|
+
}),
|
|
60
|
+
}),
|
|
35
61
|
});
|
|
36
62
|
|
|
37
63
|
export function lookupHostDescription(host: string): AcpHostDescription | undefined {
|