@staix/agent-hub 0.25.0 → 0.26.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 +10 -0
- package/README.md +10 -5
- package/docs/agent-notes/README.md +12 -0
- package/docs/agent-notes/adapters.md +1 -1
- package/docs/agent-notes/daemon.md +6 -4
- package/docs/agent-notes/tasks.md +5 -4
- package/docs/agent-notes/tests.md +1 -0
- package/docs/dogfood.md +4 -0
- package/docs/operations.md +131 -14
- package/docs/quickstart.md +14 -4
- package/docs/security.md +51 -8
- package/docs/specs/2026-11-01-research-campaigns-design.md +9 -6
- package/docs/verification/2026-11-01-0.26.0.md +54 -0
- package/docs/verified.json +28 -16
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +182 -95
- package/src/adapters/channel-peer.ts +4 -2
- package/src/adapters/definitions/builtin-metadata.ts +6 -6
- package/src/adapters/definitions/qwen.ts +2 -0
- package/src/adapters/registry-types.ts +2 -0
- package/src/adapters/registry.ts +1 -0
- package/src/cli/conductor-recovery.ts +3 -2
- package/src/cli/help.ts +7 -7
- package/src/cli/main.ts +62 -23
- package/src/cli/recovery-package.ts +94 -4
- package/src/cli/reset.ts +14 -4
- package/src/cli/runtime-version-warning.ts +28 -0
- package/src/cli/start-agents-run.ts +125 -0
- package/src/cli/task-output.ts +5 -0
- package/src/cli/upgrade-interactive.ts +4 -4
- package/src/cli/upgrade-runtime.ts +105 -57
- package/src/cli/upgrade.ts +90 -28
- package/src/hub/autonomy-defaults.ts +32 -7
- package/src/hub/board.ts +129 -13
- package/src/hub/budget.ts +3 -4
- package/src/hub/child-process.ts +4 -3
- package/src/hub/conductor.ts +3 -4
- package/src/hub/config-apply.ts +13 -2
- package/src/hub/config-edit-host.ts +1 -0
- package/src/hub/config-tier.ts +3 -0
- package/src/hub/config-trust.ts +10 -2
- package/src/hub/control-client.ts +30 -10
- package/src/hub/daemon.ts +82 -17
- package/src/hub/db-schema.ts +143 -0
- package/src/hub/delivery-journal.ts +3 -4
- package/src/hub/execution-budget.ts +3 -4
- package/src/hub/folder-trigger.ts +3 -4
- package/src/hub/invocations.ts +3 -4
- package/src/hub/lifecycle.ts +87 -29
- package/src/hub/manager.ts +49 -33
- package/src/hub/orca-env.ts +19 -0
- package/src/hub/project-runtime-state.ts +139 -0
- package/src/hub/project-runtime.ts +62 -0
- package/src/hub/protocol-window.ts +19 -0
- package/src/hub/recovery-store.ts +102 -12
- package/src/hub/registry.ts +5 -0
- package/src/hub/research-experiments.ts +33 -4
- package/src/hub/research-runner.ts +172 -0
- package/src/hub/restart.ts +9 -0
- package/src/hub/runtime-capture-prune.ts +415 -0
- package/src/hub/runtime-prune-health.ts +112 -0
- package/src/hub/schedules.ts +3 -4
- package/src/hub/sdlc-merge.ts +7 -0
- package/src/hub/sdlc-queue.ts +13 -1
- package/src/hub/settings.ts +4 -0
- package/src/hub/snapshots.ts +3 -4
- package/src/hub/start-agents.ts +44 -0
- package/src/hub/task-approval.ts +23 -0
- package/src/hub/tasks.ts +133 -34
- package/src/hub/usage-rollup.ts +3 -4
- package/src/pi/tool-receipts.ts +3 -4
|
@@ -806,6 +806,12 @@ var init_agy_mcp = __esm(() => {
|
|
|
806
806
|
MAX_CONFIG_BYTES = 256 * 1024;
|
|
807
807
|
});
|
|
808
808
|
|
|
809
|
+
// src/hub/orca-env.ts
|
|
810
|
+
var ORCA_LAUNCHER_ENV;
|
|
811
|
+
var init_orca_env = __esm(() => {
|
|
812
|
+
ORCA_LAUNCHER_ENV = Object.freeze(["ORCA_CLI_COMMAND", "ORCA_DEV_REPO_ROOT", "ORCA_TERMINAL_HANDLE", "ORCA_WORKTREE_ID"]);
|
|
813
|
+
});
|
|
814
|
+
|
|
809
815
|
// src/hub/child-process.ts
|
|
810
816
|
function childEnv(source = process.env) {
|
|
811
817
|
const env = { ...source };
|
|
@@ -1066,6 +1072,7 @@ var PS, PS_OPTIONS, own = (text) => {
|
|
|
1066
1072
|
proc.stderr?.destroy();
|
|
1067
1073
|
}, emptied;
|
|
1068
1074
|
var init_child_process = __esm(() => {
|
|
1075
|
+
init_orca_env();
|
|
1069
1076
|
PS = ["ps", "-axo", "pid=,ppid=,pgid=,stat=,lstart=,command="];
|
|
1070
1077
|
PS_OPTIONS = { env: { ...process.env, LC_ALL: "C", TZ: "UTC" }, detached: true };
|
|
1071
1078
|
emptied = new WeakSet;
|
|
@@ -5896,8 +5903,13 @@ var init_launch = __esm(() => {
|
|
|
5896
5903
|
init_child_process();
|
|
5897
5904
|
});
|
|
5898
5905
|
|
|
5906
|
+
// src/hub/recovery-error.ts
|
|
5907
|
+
var init_recovery_error = __esm(() => {
|
|
5908
|
+
init_envelope();
|
|
5909
|
+
});
|
|
5910
|
+
|
|
5899
5911
|
// src/hub/recovery-store.ts
|
|
5900
|
-
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
5912
|
+
import { chmodSync, existsSync as existsSync5, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
5901
5913
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5902
5914
|
function atomicPrivateJSON(file, value) {
|
|
5903
5915
|
const temporary = `${file}.${randomUUID4()}.tmp`;
|
|
@@ -5912,6 +5924,7 @@ function atomicPrivateJSON(file, value) {
|
|
|
5912
5924
|
}
|
|
5913
5925
|
var init_recovery_store = __esm(() => {
|
|
5914
5926
|
init_project();
|
|
5927
|
+
init_recovery_error();
|
|
5915
5928
|
init_process_signature();
|
|
5916
5929
|
});
|
|
5917
5930
|
|
|
@@ -5985,11 +5998,11 @@ var init_permission_mode = __esm(() => {
|
|
|
5985
5998
|
});
|
|
5986
5999
|
|
|
5987
6000
|
// src/hub/path-policy.ts
|
|
5988
|
-
import { accessSync, constants as constants4, lstatSync as
|
|
6001
|
+
import { accessSync, constants as constants4, lstatSync as lstatSync9, opendirSync, readlinkSync, statSync as statSync2 } from "fs";
|
|
5989
6002
|
import { dirname as dirname7, isAbsolute as isAbsolute5, join as join11, relative as relative3, resolve as resolve8, sep as sep2 } from "path";
|
|
5990
6003
|
function existing(path) {
|
|
5991
6004
|
try {
|
|
5992
|
-
|
|
6005
|
+
lstatSync9(path);
|
|
5993
6006
|
return true;
|
|
5994
6007
|
} catch (error) {
|
|
5995
6008
|
if (error.code === "ENOENT")
|
|
@@ -6038,7 +6051,7 @@ function directAliasTarget(path) {
|
|
|
6038
6051
|
return;
|
|
6039
6052
|
ancestor = parent;
|
|
6040
6053
|
}
|
|
6041
|
-
if (!
|
|
6054
|
+
if (!lstatSync9(ancestor).isSymbolicLink())
|
|
6042
6055
|
return;
|
|
6043
6056
|
target = resolve8(dirname7(ancestor), readlinkSync(ancestor), relative3(ancestor, target));
|
|
6044
6057
|
}
|
|
@@ -6515,7 +6528,7 @@ var init_text_projection = __esm(() => {
|
|
|
6515
6528
|
});
|
|
6516
6529
|
|
|
6517
6530
|
// src/local/doc-read.ts
|
|
6518
|
-
import { lstatSync as
|
|
6531
|
+
import { lstatSync as lstatSync10 } from "fs";
|
|
6519
6532
|
import { extname } from "path";
|
|
6520
6533
|
function docRead(file, shown, args, port, cwd) {
|
|
6521
6534
|
const projections = port.projections();
|
|
@@ -6525,7 +6538,7 @@ function docRead(file, shown, args, port, cwd) {
|
|
|
6525
6538
|
}
|
|
6526
6539
|
let st;
|
|
6527
6540
|
try {
|
|
6528
|
-
st =
|
|
6541
|
+
st = lstatSync10(file);
|
|
6529
6542
|
} catch {
|
|
6530
6543
|
return `error: ${shown} cannot be read`;
|
|
6531
6544
|
}
|
|
@@ -6848,7 +6861,7 @@ var init_web_fetch = __esm(() => {
|
|
|
6848
6861
|
});
|
|
6849
6862
|
|
|
6850
6863
|
// src/local/tools.ts
|
|
6851
|
-
import { lstatSync as
|
|
6864
|
+
import { lstatSync as lstatSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync7, statSync as statSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
6852
6865
|
import { dirname as dirname10, join as join13, relative as relative4, resolve as resolve10 } from "path";
|
|
6853
6866
|
function guardPath(ctx, path, mode) {
|
|
6854
6867
|
const root = realPath(ctx.cwd);
|
|
@@ -6871,7 +6884,7 @@ function guardPath(ctx, path, mode) {
|
|
|
6871
6884
|
throw new Error(`${path} is on the secrets denylist`);
|
|
6872
6885
|
if (mode === "write" && (isHubWritePath(rel) || inside && isHubWritePath(typed)))
|
|
6873
6886
|
throw new Error(`${path} is not writable by the local worker`);
|
|
6874
|
-
if (mode === "write" && ctx.readOnly?.length && (
|
|
6887
|
+
if (mode === "write" && ctx.readOnly?.length && (lstatSync11(real, { throwIfNoEntry: false })?.nlink ?? 1) > 1)
|
|
6875
6888
|
throw new Error("hardlinked write target cannot be verified with data.readonly");
|
|
6876
6889
|
if (mode === "write" && matchesPolicy(root, path, ctx.readOnly ?? []))
|
|
6877
6890
|
throw new Error("target is protected by data.readonly");
|
|
@@ -7045,7 +7058,7 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path), a),
|
|
|
7045
7058
|
}
|
|
7046
7059
|
var APPROVAL_EXPIRED_TEXT = "error: approval expired: no person answered; do not retry this call; hand the task off or stop", ApprovalWaitStop, approvalError = (picked, denied) => picked === true ? undefined : picked === "expired" ? APPROVAL_EXPIRED_TEXT : picked === "aborted" ? "error: approval withdrawn because the turn ended; no operation was executed" : denied, lexists = (p) => {
|
|
7047
7060
|
try {
|
|
7048
|
-
|
|
7061
|
+
lstatSync11(p);
|
|
7049
7062
|
return true;
|
|
7050
7063
|
} catch {
|
|
7051
7064
|
return false;
|
|
@@ -10695,7 +10708,7 @@ var init_shipped_roles = __esm(() => {
|
|
|
10695
10708
|
|
|
10696
10709
|
// src/hub/roles.ts
|
|
10697
10710
|
import { createHash as createHash6 } from "crypto";
|
|
10698
|
-
import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSync5, lstatSync as
|
|
10711
|
+
import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSync5, lstatSync as lstatSync12, openSync as openSync6, readSync as readSync4, readdirSync as readdirSync2 } from "fs";
|
|
10699
10712
|
import { join as join15 } from "path";
|
|
10700
10713
|
function parseRole(text, stem, layer) {
|
|
10701
10714
|
if (Buffer.byteLength(text) > 8192 || text.includes("\r") || !ID.test(stem))
|
|
@@ -10777,7 +10790,7 @@ function loadRoleCatalog(cwd) {
|
|
|
10777
10790
|
try {
|
|
10778
10791
|
const config = join15(cwd, ".agenthub"), dir = join15(config, "roles");
|
|
10779
10792
|
for (const path of [config, dir]) {
|
|
10780
|
-
const stat =
|
|
10793
|
+
const stat = lstatSync12(path);
|
|
10781
10794
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
10782
10795
|
throw new Error("unsafe role directory");
|
|
10783
10796
|
}
|
|
@@ -10871,6 +10884,14 @@ var init_packs = __esm(() => {
|
|
|
10871
10884
|
init_workflow();
|
|
10872
10885
|
});
|
|
10873
10886
|
|
|
10887
|
+
// src/hub/research-runner.ts
|
|
10888
|
+
var BODY, RUNNER_RECEIPT_DOMAIN;
|
|
10889
|
+
var init_research_runner = __esm(() => {
|
|
10890
|
+
init_packs();
|
|
10891
|
+
BODY = 64 * 1024;
|
|
10892
|
+
RUNNER_RECEIPT_DOMAIN = "agent-hub.runner-receipt/v1" + String.fromCharCode(0);
|
|
10893
|
+
});
|
|
10894
|
+
|
|
10874
10895
|
// node_modules/@staix/agent-mem/src/memory/mutation.ts
|
|
10875
10896
|
var mutations;
|
|
10876
10897
|
var init_mutation = __esm(() => {
|
|
@@ -11890,12 +11911,13 @@ var init_deliverables = __esm(() => {
|
|
|
11890
11911
|
});
|
|
11891
11912
|
|
|
11892
11913
|
// src/hub/research-experiments.ts
|
|
11893
|
-
var
|
|
11914
|
+
var BODY2;
|
|
11894
11915
|
var init_research_experiments = __esm(() => {
|
|
11895
11916
|
init_src();
|
|
11896
11917
|
init_packs();
|
|
11918
|
+
init_research_runner();
|
|
11897
11919
|
init_deliverables();
|
|
11898
|
-
|
|
11920
|
+
BODY2 = 64 * 1024;
|
|
11899
11921
|
});
|
|
11900
11922
|
|
|
11901
11923
|
// src/hub/task-code-symbols.ts
|
|
@@ -11904,24 +11926,98 @@ var init_task_code_symbols = __esm(() => {
|
|
|
11904
11926
|
HOST_CODE_SYMBOLS = Symbol("host task code symbols");
|
|
11905
11927
|
});
|
|
11906
11928
|
|
|
11929
|
+
// package.json
|
|
11930
|
+
var package_default;
|
|
11931
|
+
var init_package = __esm(() => {
|
|
11932
|
+
package_default = {
|
|
11933
|
+
name: "@staix/agent-hub",
|
|
11934
|
+
version: "0.26.0",
|
|
11935
|
+
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
11936
|
+
license: "MIT",
|
|
11937
|
+
type: "module",
|
|
11938
|
+
bin: {
|
|
11939
|
+
ahub: "src/cli/main.js",
|
|
11940
|
+
"agent-hub": "src/cli/main.js"
|
|
11941
|
+
},
|
|
11942
|
+
scripts: {
|
|
11943
|
+
check: "scripts/check.sh",
|
|
11944
|
+
build: "bun scripts/build.mjs",
|
|
11945
|
+
test: "bun test"
|
|
11946
|
+
},
|
|
11947
|
+
engines: {
|
|
11948
|
+
bun: ">=1.4.2"
|
|
11949
|
+
},
|
|
11950
|
+
dependencies: {
|
|
11951
|
+
"@staix/agent-mem": "0.18.0"
|
|
11952
|
+
},
|
|
11953
|
+
devDependencies: {
|
|
11954
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
11955
|
+
"@types/bun": "^1.3.0",
|
|
11956
|
+
typescript: "^5.8.0"
|
|
11957
|
+
},
|
|
11958
|
+
files: [
|
|
11959
|
+
".claude-plugin",
|
|
11960
|
+
"src",
|
|
11961
|
+
"src/pet/window.jxa.js",
|
|
11962
|
+
"src/pet/window.html",
|
|
11963
|
+
"plugins",
|
|
11964
|
+
"templates",
|
|
11965
|
+
"scripts/check-docs.mjs",
|
|
11966
|
+
"docs",
|
|
11967
|
+
"README.md",
|
|
11968
|
+
"LICENSE",
|
|
11969
|
+
"CHANGELOG.md",
|
|
11970
|
+
"THIRD_PARTY_NOTICES.md",
|
|
11971
|
+
"LICENSES"
|
|
11972
|
+
],
|
|
11973
|
+
repository: {
|
|
11974
|
+
type: "git",
|
|
11975
|
+
url: "git+https://github.com/STAIxBWLB/agent-hub.git"
|
|
11976
|
+
},
|
|
11977
|
+
homepage: "https://github.com/STAIxBWLB/agent-hub",
|
|
11978
|
+
publishConfig: {
|
|
11979
|
+
access: "public"
|
|
11980
|
+
},
|
|
11981
|
+
exports: {
|
|
11982
|
+
"./adapter": "./src/adapters/public.ts",
|
|
11983
|
+
"./conformance": "./src/adapters/conformance/index.ts",
|
|
11984
|
+
"./ui-plugin": "./src/ui/plugin.ts"
|
|
11985
|
+
}
|
|
11986
|
+
};
|
|
11987
|
+
});
|
|
11988
|
+
|
|
11989
|
+
// src/version.ts
|
|
11990
|
+
var VERSION;
|
|
11991
|
+
var init_version = __esm(() => {
|
|
11992
|
+
init_package();
|
|
11993
|
+
VERSION = package_default.version;
|
|
11994
|
+
});
|
|
11995
|
+
// src/hub/db-schema.ts
|
|
11996
|
+
var init_db_schema = __esm(() => {
|
|
11997
|
+
init_version();
|
|
11998
|
+
});
|
|
11999
|
+
|
|
11907
12000
|
// src/hub/planning.ts
|
|
11908
12001
|
var init_planning = __esm(() => {
|
|
11909
12002
|
init_sdlc();
|
|
11910
12003
|
});
|
|
12004
|
+
|
|
11911
12005
|
// src/hub/research-campaign.ts
|
|
11912
|
-
var
|
|
12006
|
+
var BODY3;
|
|
11913
12007
|
var init_research_campaign = __esm(() => {
|
|
11914
12008
|
init_packs();
|
|
11915
|
-
|
|
12009
|
+
BODY3 = 64 * 1024;
|
|
11916
12010
|
});
|
|
11917
12011
|
|
|
11918
12012
|
// src/hub/board.ts
|
|
11919
12013
|
var CLASSES2, PLAN_KEYS, ALL_PLAN_KEYS, OUTCOMES_KEPT_MS;
|
|
11920
12014
|
var init_board = __esm(() => {
|
|
12015
|
+
init_research_runner();
|
|
11921
12016
|
init_packs();
|
|
11922
12017
|
init_research_experiments();
|
|
11923
12018
|
init_task_code_symbols();
|
|
11924
12019
|
init_plan_conformance();
|
|
12020
|
+
init_db_schema();
|
|
11925
12021
|
init_planning();
|
|
11926
12022
|
init_envelope();
|
|
11927
12023
|
init_review_findings();
|
|
@@ -13012,7 +13108,7 @@ var WITHHELD = "Claude implementer turn failed (details withheld)";
|
|
|
13012
13108
|
|
|
13013
13109
|
// src/adapters/claude-implementer.ts
|
|
13014
13110
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
13015
|
-
import { lstatSync as
|
|
13111
|
+
import { lstatSync as lstatSync13, mkdtempSync as mkdtempSync5, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
13016
13112
|
import { tmpdir as tmpdir5 } from "os";
|
|
13017
13113
|
import { join as join17 } from "path";
|
|
13018
13114
|
function provenVersion(binding) {
|
|
@@ -13070,7 +13166,7 @@ function claudeImplementerArgs(turn, mcp, settings, prompt, model) {
|
|
|
13070
13166
|
async function proveClaudeImplementer(peer, command) {
|
|
13071
13167
|
const binary = Bun.which(command[0], { PATH: process.env.PATH }) ?? command[0];
|
|
13072
13168
|
const fingerprint = () => {
|
|
13073
|
-
const file = realPath(binary), stat =
|
|
13169
|
+
const file = realPath(binary), stat = lstatSync13(file);
|
|
13074
13170
|
return [file, stat.dev, stat.ino, stat.size, stat.mtimeMs, stat.ctimeMs].join(":");
|
|
13075
13171
|
};
|
|
13076
13172
|
const binding = fingerprint(), home = mkdtempSync5(join17(tmpdir5(), "ahub-claude-proof-"));
|
|
@@ -13625,7 +13721,7 @@ var init_reviewer_variants = __esm(() => {
|
|
|
13625
13721
|
});
|
|
13626
13722
|
|
|
13627
13723
|
// src/adapters/agy/hooks.ts
|
|
13628
|
-
import { closeSync as closeSync7, constants as constants6, fstatSync as fstatSync6, lstatSync as
|
|
13724
|
+
import { closeSync as closeSync7, constants as constants6, fstatSync as fstatSync6, lstatSync as lstatSync14, openSync as openSync7, readSync as readSync5 } from "fs";
|
|
13629
13725
|
import { homedir as homedir5 } from "os";
|
|
13630
13726
|
import { isAbsolute as isAbsolute6, join as join18, relative as relative5 } from "path";
|
|
13631
13727
|
import { createHash as createHash7 } from "crypto";
|
|
@@ -13673,7 +13769,7 @@ function readAgyTranscript(session, transcript) {
|
|
|
13673
13769
|
let cursor = home;
|
|
13674
13770
|
for (const name of relative5(home, transcript).split("/")) {
|
|
13675
13771
|
cursor = join18(cursor, name);
|
|
13676
|
-
if (
|
|
13772
|
+
if (lstatSync14(cursor).isSymbolicLink())
|
|
13677
13773
|
return empty;
|
|
13678
13774
|
}
|
|
13679
13775
|
fd = openSync7(transcript, constants6.O_RDONLY | constants6.O_NOFOLLOW);
|
|
@@ -15107,6 +15203,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
15107
15203
|
version: claudeObservedVersion,
|
|
15108
15204
|
...CLAUDE_LAUNCH_POLICY,
|
|
15109
15205
|
conductorStartable: true,
|
|
15206
|
+
upStart: { allowed: true, default: true },
|
|
15110
15207
|
unattended: true,
|
|
15111
15208
|
hubSwitches: switches,
|
|
15112
15209
|
ownedFlags: Object.freeze(["--dangerously-load-development-channels", "--dangerously-skip-permissions"])
|
|
@@ -15148,6 +15245,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
15148
15245
|
version: codexObservedVersion,
|
|
15149
15246
|
...CODEX_LAUNCH_POLICY,
|
|
15150
15247
|
conductorStartable: true,
|
|
15248
|
+
upStart: { allowed: true, default: true },
|
|
15151
15249
|
unattended: true,
|
|
15152
15250
|
hubSwitches: switches,
|
|
15153
15251
|
ownedFlags: Object.freeze(["--remote", "--remote-auth-token-env", "--yolo", "--dangerously-bypass-approvals-and-sandbox"])
|
|
@@ -15173,7 +15271,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
15173
15271
|
roles: implementerRoles,
|
|
15174
15272
|
defaultOnRoster: true,
|
|
15175
15273
|
trust: { onPrem: false, hubSandboxed: false },
|
|
15176
|
-
start: { create: createKimi, acpCommand: kimiAcpCommand, explicitHeadlessMode: true, planCommand: () => "ahub kimi", daemonPort: "acp", forms: dualForms, defaultForm: "tui", conductorStartable: true, hubSwitches: [], ownedFlags: [] },
|
|
15274
|
+
start: { create: createKimi, acpCommand: kimiAcpCommand, explicitHeadlessMode: true, planCommand: () => "ahub kimi", daemonPort: "acp", forms: dualForms, defaultForm: "tui", conductorStartable: true, upStart: { allowed: true }, hubSwitches: [], ownedFlags: [] },
|
|
15177
15275
|
identity: { mcp: "acp-session" },
|
|
15178
15276
|
instances: { mode: "headless" },
|
|
15179
15277
|
attach: {
|
|
@@ -15207,7 +15305,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
15207
15305
|
roles: implementerRoles,
|
|
15208
15306
|
defaultOnRoster: false,
|
|
15209
15307
|
trust: { onPrem: false, hubSandboxed: true },
|
|
15210
|
-
start: { create: createPi, exclusiveStartRefusal: "Pi start is in progress; inspect status before retrying", refreshTools: true, explicitHeadlessMode: true, daemonPort: "pi-rpc", forms: dualForms, defaultForm: "tui", conductorStartable: true, autoStart: "pi.auto_start", conductorStartArgs: piConductorStartArgs, hubSwitches: [], ownedFlags: [] },
|
|
15308
|
+
start: { create: createPi, exclusiveStartRefusal: "Pi start is in progress; inspect status before retrying", refreshTools: true, explicitHeadlessMode: true, daemonPort: "pi-rpc", forms: dualForms, defaultForm: "tui", conductorStartable: true, upStart: { allowed: true }, autoStart: "pi.auto_start", conductorStartArgs: piConductorStartArgs, hubSwitches: [], ownedFlags: [] },
|
|
15211
15309
|
identity: { mcp: "env" },
|
|
15212
15310
|
instances: { mode: "headless" },
|
|
15213
15311
|
permission: { grants: { ...hubGrants } },
|
|
@@ -15230,7 +15328,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
15230
15328
|
roles: implementerRoles,
|
|
15231
15329
|
defaultOnRoster: true,
|
|
15232
15330
|
trust: { onPrem: true, hubSandboxed: true },
|
|
15233
|
-
start: { create: createLocal, reconfigureWhileAttached: true, refreshTools: true, daemonPort: "local-worker", forms: headlessForms, defaultForm: "headless", conductorStartable: true, hubSwitches: [], ownedFlags: [] },
|
|
15331
|
+
start: { create: createLocal, reconfigureWhileAttached: true, refreshTools: true, daemonPort: "local-worker", forms: headlessForms, defaultForm: "headless", conductorStartable: true, upStart: { allowed: true }, hubSwitches: [], ownedFlags: [] },
|
|
15234
15332
|
identity: { mcp: "env" },
|
|
15235
15333
|
instances: { mode: "headless" },
|
|
15236
15334
|
permission: { grants: { ...hubGrants } },
|
|
@@ -15253,7 +15351,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
15253
15351
|
optional: true,
|
|
15254
15352
|
defaultOnRoster: false,
|
|
15255
15353
|
trust: { onPrem: false, hubSandboxed: false },
|
|
15256
|
-
start: { create: createJcode, preflight: jcodeStartPreflight, daemonPort: "owned-acp", forms: dualForms, defaultForm: "tui", explicitHeadlessMode: true, conductorStartable: true, hubSwitches: [], ownedFlags: [] },
|
|
15354
|
+
start: { create: createJcode, preflight: jcodeStartPreflight, daemonPort: "owned-acp", forms: dualForms, defaultForm: "tui", explicitHeadlessMode: true, conductorStartable: true, upStart: { allowed: true }, hubSwitches: [], ownedFlags: [] },
|
|
15257
15355
|
identity: { mcp: "project-file" },
|
|
15258
15356
|
instances: { mode: "headless", nativeHome: true },
|
|
15259
15357
|
permission: { unmanaged: "Jcode native policy is unmanaged; no hub mode mapping", grants: {
|
|
@@ -15411,6 +15509,7 @@ var init_qwen = __esm(() => {
|
|
|
15411
15509
|
forms: ["headless"],
|
|
15412
15510
|
defaultForm: "headless",
|
|
15413
15511
|
conductorStartable: true,
|
|
15512
|
+
upStart: { allowed: false, refusal: "the hub never opens its TUI, and a Qwen is started only by ahub qwen" },
|
|
15414
15513
|
hubSwitches: [],
|
|
15415
15514
|
ownedFlags: [],
|
|
15416
15515
|
preflight: (request) => request.form !== undefined && request.form !== "headless" && request.form !== "tui" ? "unknown Qwen mode; usage: ahub qwen [--mode headless|tui]" : undefined,
|
|
@@ -15838,6 +15937,7 @@ function assertNativeCapabilityScope(definition) {
|
|
|
15838
15937
|
["start.daemonPort pi-rpc", definition.start.daemonPort === "pi-rpc"],
|
|
15839
15938
|
["start.daemonPort qwen-dual", definition.start.daemonPort === "qwen-dual"],
|
|
15840
15939
|
["start.createTui", declared(definition.start, "createTui")],
|
|
15940
|
+
["start.upStart", declared(definition.start, "upStart")],
|
|
15841
15941
|
["routing", definition.routing !== undefined],
|
|
15842
15942
|
["projection", definition.projection !== undefined],
|
|
15843
15943
|
["instances.nativeHome", declared(definition.instances, "nativeHome")],
|
|
@@ -22924,7 +23024,7 @@ init_registry();
|
|
|
22924
23024
|
// src/hub/config-trust.ts
|
|
22925
23025
|
init_child_process();
|
|
22926
23026
|
init_git();
|
|
22927
|
-
import { lstatSync as
|
|
23027
|
+
import { lstatSync as lstatSync15, statSync as statSync5 } from "fs";
|
|
22928
23028
|
import { dirname as dirname12, join as join28 } from "path";
|
|
22929
23029
|
var MACHINE_LOCAL = [
|
|
22930
23030
|
"kimi_cmd",
|
|
@@ -22953,7 +23053,7 @@ var MACHINE_LOCAL = [
|
|
|
22953
23053
|
"corpus.paths",
|
|
22954
23054
|
"invocation"
|
|
22955
23055
|
];
|
|
22956
|
-
var ALL_MACHINE_LOCAL = [...MACHINE_LOCAL, "rooms", "tasks.queue_backpressure", "context.max_assignment_used", "action_policy", "protect_instruction_files", "qwen_cmd", "jcode_cmd", "jcode_home", "jcode_provider_profile", "adapters", "adapter_modules", "text_projection", "instances"];
|
|
23056
|
+
var ALL_MACHINE_LOCAL = [...MACHINE_LOCAL, "research.runners", "rooms", "tasks.queue_backpressure", "context.max_assignment_used", "action_policy", "protect_instruction_files", "qwen_cmd", "jcode_cmd", "jcode_home", "jcode_provider_profile", "start.agents", "adapters", "adapter_modules", "text_projection", "instances"];
|
|
22957
23057
|
function configRefusal(cwd, name) {
|
|
22958
23058
|
const tracked = configTracked(cwd, name);
|
|
22959
23059
|
return tracked === undefined ? `git could not confirm that .agenthub/${name} is untracked` : tracked ? `.agenthub/${name} is committed to git` : undefined;
|
|
@@ -23032,11 +23132,40 @@ function readAdapterModuleDeclarations(cwd, layers) {
|
|
|
23032
23132
|
init_project();
|
|
23033
23133
|
import { readFileSync as readFileSync14 } from "fs";
|
|
23034
23134
|
import { join as join29, resolve as resolve17 } from "path";
|
|
23135
|
+
|
|
23136
|
+
// src/hub/protocol-window.ts
|
|
23137
|
+
function protocolWindow(v, vMin) {
|
|
23138
|
+
if (typeof v !== "number" || !Number.isSafeInteger(v) || v <= 0)
|
|
23139
|
+
return;
|
|
23140
|
+
const minimum = vMin === undefined ? v : vMin;
|
|
23141
|
+
if (typeof minimum !== "number" || !Number.isSafeInteger(minimum) || minimum <= 0 || minimum > v)
|
|
23142
|
+
return;
|
|
23143
|
+
return { v, vMin: minimum };
|
|
23144
|
+
}
|
|
23145
|
+
function negotiateProtocol(local, remote) {
|
|
23146
|
+
if (!local || typeof local !== "object" || Array.isArray(local) || !remote || typeof remote !== "object" || Array.isArray(remote))
|
|
23147
|
+
return;
|
|
23148
|
+
const a = protocolWindow(local.v, local.vMin), b = protocolWindow(remote.v, remote.vMin);
|
|
23149
|
+
if (!a || !b)
|
|
23150
|
+
return;
|
|
23151
|
+
const highest = Math.min(a.v, b.v);
|
|
23152
|
+
return highest >= Math.max(a.vMin, b.vMin) ? highest : undefined;
|
|
23153
|
+
}
|
|
23154
|
+
|
|
23155
|
+
// src/hub/control-client.ts
|
|
23035
23156
|
function stateDirFor(cwd) {
|
|
23036
23157
|
return projectContext(cwd).stateDir;
|
|
23037
23158
|
}
|
|
23038
|
-
var PROTOCOL =
|
|
23039
|
-
var
|
|
23159
|
+
var PROTOCOL = 17;
|
|
23160
|
+
var PROTOCOL_MIN = 16;
|
|
23161
|
+
var RECOVERY_SOURCE_PROTOCOLS = [9, 10, 11, 12, 13, 14, 15, 16, PROTOCOL];
|
|
23162
|
+
function controlProtocolWindow(descriptor) {
|
|
23163
|
+
const legacy = protocolWindow(descriptor.protocol);
|
|
23164
|
+
const window = protocolWindow(descriptor.protocolMax ?? descriptor.protocol, descriptor.protocolMin ?? descriptor.protocol);
|
|
23165
|
+
if (!legacy || !window || legacy.v < window.vMin || legacy.v > window.v)
|
|
23166
|
+
return;
|
|
23167
|
+
return window;
|
|
23168
|
+
}
|
|
23040
23169
|
function readControl(stateDir) {
|
|
23041
23170
|
try {
|
|
23042
23171
|
const status = JSON.parse(readFileSync14(join29(stateDir, "status.json"), "utf8"));
|
|
@@ -23051,6 +23180,8 @@ function readControl(stateDir) {
|
|
|
23051
23180
|
instanceId: status.instanceId,
|
|
23052
23181
|
cwd: status.cwd,
|
|
23053
23182
|
protocol: status.protocol,
|
|
23183
|
+
protocolMin: status.protocolMin,
|
|
23184
|
+
protocolMax: status.protocolMax,
|
|
23054
23185
|
pid: status.pid,
|
|
23055
23186
|
features: Array.isArray(status.features) ? status.features.filter((feature) => typeof feature === "string") : [],
|
|
23056
23187
|
...startModes ? { startModes } : {},
|
|
@@ -23071,6 +23202,7 @@ class ControlClient {
|
|
|
23071
23202
|
ws;
|
|
23072
23203
|
features = [];
|
|
23073
23204
|
sessionId;
|
|
23205
|
+
protocol;
|
|
23074
23206
|
welcomeInstanceId;
|
|
23075
23207
|
get instanceId() {
|
|
23076
23208
|
return this.welcomeInstanceId;
|
|
@@ -23097,8 +23229,8 @@ class ControlClient {
|
|
|
23097
23229
|
instancePins.delete(key);
|
|
23098
23230
|
};
|
|
23099
23231
|
}
|
|
23100
|
-
static connect(stateDir, hello, timeoutMs = 3000, protocol
|
|
23101
|
-
if (!RECOVERY_SOURCE_PROTOCOLS.includes(protocol))
|
|
23232
|
+
static connect(stateDir, hello, timeoutMs = 3000, protocol) {
|
|
23233
|
+
if (protocol !== undefined && !RECOVERY_SOURCE_PROTOCOLS.includes(protocol))
|
|
23102
23234
|
return Promise.reject(new Error(`unsupported recovery source protocol ${protocol}`));
|
|
23103
23235
|
const control = readControl(stateDir);
|
|
23104
23236
|
if (!control)
|
|
@@ -23107,9 +23239,13 @@ class ControlClient {
|
|
|
23107
23239
|
if (pin && (control.instanceId !== pin.instanceId || hello.instanceId !== undefined && hello.instanceId !== pin.instanceId)) {
|
|
23108
23240
|
return Promise.reject(new Error("delegated audit consumer changed; refusing a connection to its replacement"));
|
|
23109
23241
|
}
|
|
23110
|
-
|
|
23111
|
-
|
|
23112
|
-
|
|
23242
|
+
const local = protocolWindow(protocol ?? PROTOCOL, protocol ?? PROTOCOL_MIN);
|
|
23243
|
+
const versioned = control.protocol !== undefined || control.protocolMin !== undefined || control.protocolMax !== undefined;
|
|
23244
|
+
const remote = versioned ? controlProtocolWindow(control) : undefined;
|
|
23245
|
+
const selected = versioned ? remote && negotiateProtocol(local, remote) : protocol ?? PROTOCOL_MIN;
|
|
23246
|
+
const mismatch = () => Object.assign(new Error(`wire version mismatch: hub speaks ${control.protocolMax ?? control.protocol}, CLI speaks ${protocol ?? PROTOCOL}; stop it with its matching CLI, then upgrade and restart`), { code: 4426 });
|
|
23247
|
+
if (selected === undefined)
|
|
23248
|
+
return Promise.reject(mismatch());
|
|
23113
23249
|
if (hello.projectId && hello.projectId !== control.projectId || hello.instanceId && hello.instanceId !== control.instanceId || hello.projectRoot && hello.projectRoot !== control.cwd) {
|
|
23114
23250
|
return Promise.reject(Object.assign(new Error("hub project or instance does not match the selected project"), { code: 4404 }));
|
|
23115
23251
|
}
|
|
@@ -23151,12 +23287,15 @@ class ControlClient {
|
|
|
23151
23287
|
client.pending.delete(msg.rid);
|
|
23152
23288
|
done(msg);
|
|
23153
23289
|
};
|
|
23154
|
-
ws.onopen = () => void client.request({ t: "hello",
|
|
23290
|
+
ws.onopen = () => void client.request({ t: "hello", token: control.token, ...hello, ...expected, v: selected, vMin: remote?.vMin === undefined ? selected : Math.max(local.vMin, remote.vMin) }, timeoutMs).then((reply) => {
|
|
23155
23291
|
if (reply.t !== "welcome" || reply.ok === false)
|
|
23156
23292
|
return refuse(new Error(reply.error ?? "hub refused handshake"));
|
|
23157
23293
|
if (expected.projectId && reply.projectId !== expected.projectId || expected.instanceId && reply.instanceId !== expected.instanceId || expected.projectRoot && reply.cwd !== expected.projectRoot) {
|
|
23158
23294
|
return refuse(Object.assign(new Error("connected hub has a different project or instance"), { code: 4404 }));
|
|
23159
23295
|
}
|
|
23296
|
+
if (reply.protocol !== undefined && reply.protocol !== selected)
|
|
23297
|
+
return refuse(mismatch());
|
|
23298
|
+
client.protocol = selected;
|
|
23160
23299
|
client.features = Object.freeze(Array.isArray(reply.features) ? reply.features.filter((feature) => typeof feature === "string") : []);
|
|
23161
23300
|
client.welcomeInstanceId = typeof reply.instanceId === "string" && reply.instanceId ? reply.instanceId : undefined;
|
|
23162
23301
|
client.sessionId = typeof reply.session === "string" && reply.session.length <= 64 ? reply.session : undefined;
|
|
@@ -23239,10 +23378,12 @@ class ControlClient {
|
|
|
23239
23378
|
|
|
23240
23379
|
// src/hub/conductor.ts
|
|
23241
23380
|
init_registry();
|
|
23381
|
+
init_db_schema();
|
|
23242
23382
|
init_hub_tools();
|
|
23243
23383
|
init_envelope();
|
|
23244
23384
|
|
|
23245
23385
|
// src/hub/tasks.ts
|
|
23386
|
+
init_research_runner();
|
|
23246
23387
|
init_queue_backpressure();
|
|
23247
23388
|
|
|
23248
23389
|
// src/hub/memory-symbols.ts
|
|
@@ -23275,7 +23416,9 @@ init_git();
|
|
|
23275
23416
|
|
|
23276
23417
|
// src/hub/tasks.ts
|
|
23277
23418
|
init_spec_read();
|
|
23419
|
+
|
|
23278
23420
|
// src/hub/schedules.ts
|
|
23421
|
+
init_db_schema();
|
|
23279
23422
|
var MIN_EVERY_MS = 15 * 60000;
|
|
23280
23423
|
var MAX_EVERY_MS = 365 * 86400000;
|
|
23281
23424
|
var KEYS = new Set(["id", "every", "cron", "tz", "class", "title", "detail", "sources", "owner", "queue"]);
|
|
@@ -23289,65 +23432,7 @@ var INTAKE_MAX_ANSWER = 64 * 1024;
|
|
|
23289
23432
|
// src/hub/tasks.ts
|
|
23290
23433
|
init_sdlc_metrics();
|
|
23291
23434
|
init_security_baseline();
|
|
23292
|
-
|
|
23293
|
-
var package_default = {
|
|
23294
|
-
name: "@staix/agent-hub",
|
|
23295
|
-
version: "0.25.0",
|
|
23296
|
-
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
23297
|
-
license: "MIT",
|
|
23298
|
-
type: "module",
|
|
23299
|
-
bin: {
|
|
23300
|
-
ahub: "src/cli/main.js",
|
|
23301
|
-
"agent-hub": "src/cli/main.js"
|
|
23302
|
-
},
|
|
23303
|
-
scripts: {
|
|
23304
|
-
check: "scripts/check.sh",
|
|
23305
|
-
build: "bun scripts/build.mjs",
|
|
23306
|
-
test: "bun test"
|
|
23307
|
-
},
|
|
23308
|
-
engines: {
|
|
23309
|
-
bun: ">=1.4.2"
|
|
23310
|
-
},
|
|
23311
|
-
dependencies: {
|
|
23312
|
-
"@staix/agent-mem": "0.18.0"
|
|
23313
|
-
},
|
|
23314
|
-
devDependencies: {
|
|
23315
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
23316
|
-
"@types/bun": "^1.3.0",
|
|
23317
|
-
typescript: "^5.8.0"
|
|
23318
|
-
},
|
|
23319
|
-
files: [
|
|
23320
|
-
".claude-plugin",
|
|
23321
|
-
"src",
|
|
23322
|
-
"src/pet/window.jxa.js",
|
|
23323
|
-
"src/pet/window.html",
|
|
23324
|
-
"plugins",
|
|
23325
|
-
"templates",
|
|
23326
|
-
"scripts/check-docs.mjs",
|
|
23327
|
-
"docs",
|
|
23328
|
-
"README.md",
|
|
23329
|
-
"LICENSE",
|
|
23330
|
-
"CHANGELOG.md",
|
|
23331
|
-
"THIRD_PARTY_NOTICES.md",
|
|
23332
|
-
"LICENSES"
|
|
23333
|
-
],
|
|
23334
|
-
repository: {
|
|
23335
|
-
type: "git",
|
|
23336
|
-
url: "git+https://github.com/STAIxBWLB/agent-hub.git"
|
|
23337
|
-
},
|
|
23338
|
-
homepage: "https://github.com/STAIxBWLB/agent-hub",
|
|
23339
|
-
publishConfig: {
|
|
23340
|
-
access: "public"
|
|
23341
|
-
},
|
|
23342
|
-
exports: {
|
|
23343
|
-
"./adapter": "./src/adapters/public.ts",
|
|
23344
|
-
"./conformance": "./src/adapters/conformance/index.ts",
|
|
23345
|
-
"./ui-plugin": "./src/ui/plugin.ts"
|
|
23346
|
-
}
|
|
23347
|
-
};
|
|
23348
|
-
|
|
23349
|
-
// src/version.ts
|
|
23350
|
-
var VERSION = package_default.version;
|
|
23435
|
+
init_version();
|
|
23351
23436
|
|
|
23352
23437
|
// src/hub/no-writes.ts
|
|
23353
23438
|
var OWNER_TOOL_EFFECTS = new Set(["hub_task_propose", "hub_task_decline", "hub_task_done", "hub_task_cancel", "hub_task_assign", "hub_task_escalate", "hub_review", "hub_checkpoint", "hub_remember", "hub_memory_correct", "hub_check_start", "hub_check_cancel", "hub_sdlc_record", "hub_plan_review", "hub_backlog_plan", "hub_backlog_pull", "hub_backlog_undo", "hub_backlog_split", "hub_backlog_return_untouched", "hub_release_new", "hub_release_configure", "hub_release_assign", "hub_release_cut", "hub_study_handoff"]);
|
|
@@ -23405,7 +23490,7 @@ var LEARNING_DEFAULTS = { windowMs: 30 * 24 * 3600000, threshold: 3, maxGroups:
|
|
|
23405
23490
|
init_deliverables();
|
|
23406
23491
|
|
|
23407
23492
|
// src/hub/deliverable-files.ts
|
|
23408
|
-
import { constants as constants8, lstatSync as
|
|
23493
|
+
import { constants as constants8, lstatSync as lstatSync16 } from "fs";
|
|
23409
23494
|
init_tools();
|
|
23410
23495
|
init_deny();
|
|
23411
23496
|
init_permission_mode();
|
|
@@ -23466,6 +23551,7 @@ init_tool_refusal();
|
|
|
23466
23551
|
// src/hub/peer-doing.ts
|
|
23467
23552
|
var TITLE_READ_MAX = 1024 * 1024;
|
|
23468
23553
|
var TITLE_LINE_MAX = 64 * 1024;
|
|
23554
|
+
|
|
23469
23555
|
// src/hub/instances.ts
|
|
23470
23556
|
import { types as types4 } from "util";
|
|
23471
23557
|
init_project();
|
|
@@ -32106,7 +32192,7 @@ init_registry();
|
|
|
32106
32192
|
// src/hub/instance-execution.ts
|
|
32107
32193
|
init_git();
|
|
32108
32194
|
init_project();
|
|
32109
|
-
import { lstatSync as
|
|
32195
|
+
import { lstatSync as lstatSync17 } from "fs";
|
|
32110
32196
|
import { join as join31, resolve as resolve19 } from "path";
|
|
32111
32197
|
var refuse = (reason) => {
|
|
32112
32198
|
throw new Error(`instance execution workspace refused: ${reason}`);
|
|
@@ -32149,7 +32235,7 @@ function resolveInstanceExecutionCwd(projectRoot, id, declarations, roles, adapt
|
|
|
32149
32235
|
try {
|
|
32150
32236
|
root = realPath(projectRoot);
|
|
32151
32237
|
cwd = realPath(resolve19(root, declaration.worktree));
|
|
32152
|
-
if (!
|
|
32238
|
+
if (!lstatSync17(root).isDirectory() || !lstatSync17(cwd).isDirectory())
|
|
32153
32239
|
return refuse("workspace is not a directory");
|
|
32154
32240
|
} catch {
|
|
32155
32241
|
return refuse("workspace paths are unavailable");
|
|
@@ -32220,6 +32306,7 @@ function channelProject(cwd, env, policyRoles) {
|
|
|
32220
32306
|
}
|
|
32221
32307
|
|
|
32222
32308
|
// src/adapters/claude-channel.ts
|
|
32309
|
+
init_version();
|
|
32223
32310
|
init_hub_tools();
|
|
32224
32311
|
init_envelope();
|
|
32225
32312
|
|
|
@@ -6,7 +6,7 @@ import type { StartForm } from "./registry-types.ts";
|
|
|
6
6
|
/** Only the control socket operations the peer owns; authentication remains in the daemon. */
|
|
7
7
|
export interface ChannelSocket {
|
|
8
8
|
readyState: number;
|
|
9
|
-
data: { launchId?: string };
|
|
9
|
+
data: { launchId?: string; protocol?: number };
|
|
10
10
|
send(data: string): unknown;
|
|
11
11
|
close(code?: number, reason?: string): void;
|
|
12
12
|
}
|
|
@@ -142,7 +142,9 @@ export class WsPeer extends BasePeer {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
recoveryMetadata(): Record<string, unknown> {
|
|
145
|
-
|
|
145
|
+
const protocol = this.attached ? this.sock?.data.protocol : undefined;
|
|
146
|
+
return { launch: definitionOf(this.id)?.attach?.recoveryLaunch?.(this.id) ?? { kind: "claude-channel", peer: this.id },
|
|
147
|
+
...(typeof protocol === "number" && Number.isSafeInteger(protocol) && protocol > 0 ? { controlProtocol: protocol } : {}) };
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
150
|
|