@massa-ai/mcp-client 1.52.0 → 1.54.1
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/config-cli.js +309 -41
- package/dist/index.js +710 -46
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -27171,17 +27171,11 @@ function detectRoute(platform, host) {
|
|
|
27171
27171
|
if (route === "file")
|
|
27172
27172
|
return { kind: "proceed" };
|
|
27173
27173
|
if (route === "marketplace") {
|
|
27174
|
-
if (host === "claude")
|
|
27174
|
+
if (host === "claude" || host === "codex")
|
|
27175
27175
|
return { kind: "proceed" };
|
|
27176
|
-
if (host === "codex") {
|
|
27177
|
-
return {
|
|
27178
|
-
kind: "refuse",
|
|
27179
|
-
reason: "codex marketplace-route installs are refused (in-place bundle rewrite would dirty a checkout " + "and break the drift gate) \u2014 use the dev path: MASSA_AI_MODEL_PROFILE + regenerate."
|
|
27180
|
-
};
|
|
27181
|
-
}
|
|
27182
27176
|
return {
|
|
27183
27177
|
kind: "refuse",
|
|
27184
|
-
reason: "
|
|
27178
|
+
reason: "marketplace-route installs need an explicit, supported host before switching " + "(claude and codex proceed; no other host resolves a live write target yet) \u2014 " + "use the dev path: MASSA_AI_MODEL_PROFILE + regenerate."
|
|
27185
27179
|
};
|
|
27186
27180
|
}
|
|
27187
27181
|
return {
|
|
@@ -27380,6 +27374,61 @@ var init_lock = __esm(() => {
|
|
|
27380
27374
|
import fs5 from "fs";
|
|
27381
27375
|
import os5 from "os";
|
|
27382
27376
|
import path9 from "path";
|
|
27377
|
+
function splitPluginKey(pluginKey) {
|
|
27378
|
+
const at = pluginKey.indexOf("@");
|
|
27379
|
+
if (at === -1)
|
|
27380
|
+
return null;
|
|
27381
|
+
const pluginName = pluginKey.slice(0, at);
|
|
27382
|
+
const marketplaceName = pluginKey.slice(at + 1);
|
|
27383
|
+
if (!pluginName || !marketplaceName)
|
|
27384
|
+
return null;
|
|
27385
|
+
return { pluginName, marketplaceName };
|
|
27386
|
+
}
|
|
27387
|
+
function isContainedPath(root, candidate) {
|
|
27388
|
+
const rel = path9.relative(root, candidate);
|
|
27389
|
+
return rel === "" || !rel.startsWith("..") && !path9.isAbsolute(rel);
|
|
27390
|
+
}
|
|
27391
|
+
function resolveDirectorySourceRoot(targetHome, pluginKey) {
|
|
27392
|
+
const split = splitPluginKey(pluginKey);
|
|
27393
|
+
if (!split)
|
|
27394
|
+
return;
|
|
27395
|
+
const { pluginName, marketplaceName } = split;
|
|
27396
|
+
const knownMarketplacesPath = path9.join(targetHome, ".claude", "plugins", "known_marketplaces.json");
|
|
27397
|
+
let known;
|
|
27398
|
+
try {
|
|
27399
|
+
known = JSON.parse(fs5.readFileSync(knownMarketplacesPath, "utf8"));
|
|
27400
|
+
} catch {
|
|
27401
|
+
return;
|
|
27402
|
+
}
|
|
27403
|
+
const entry = known?.[marketplaceName];
|
|
27404
|
+
if (!entry || entry.source?.source !== "directory")
|
|
27405
|
+
return;
|
|
27406
|
+
const installLocation = entry.installLocation;
|
|
27407
|
+
if (!installLocation)
|
|
27408
|
+
return null;
|
|
27409
|
+
const manifestPath = path9.join(installLocation, ".claude-plugin", "marketplace.json");
|
|
27410
|
+
let manifest;
|
|
27411
|
+
try {
|
|
27412
|
+
manifest = JSON.parse(fs5.readFileSync(manifestPath, "utf8"));
|
|
27413
|
+
} catch {
|
|
27414
|
+
return null;
|
|
27415
|
+
}
|
|
27416
|
+
const plugin = manifest.plugins?.find((p) => p?.name === pluginName);
|
|
27417
|
+
const relSource = plugin?.source;
|
|
27418
|
+
if (!relSource)
|
|
27419
|
+
return null;
|
|
27420
|
+
const resolvedInstallLocation = path9.resolve(installLocation);
|
|
27421
|
+
const composed = path9.resolve(resolvedInstallLocation, relSource);
|
|
27422
|
+
if (!isContainedPath(resolvedInstallLocation, composed))
|
|
27423
|
+
return null;
|
|
27424
|
+
try {
|
|
27425
|
+
if (!fs5.existsSync(composed))
|
|
27426
|
+
return null;
|
|
27427
|
+
} catch {
|
|
27428
|
+
return null;
|
|
27429
|
+
}
|
|
27430
|
+
return composed;
|
|
27431
|
+
}
|
|
27383
27432
|
function selectRecord(records) {
|
|
27384
27433
|
if (records.length === 0)
|
|
27385
27434
|
return;
|
|
@@ -27399,6 +27448,9 @@ function selectRecord(records) {
|
|
|
27399
27448
|
function resolveClaudeMarketplaceRoot(opts = {}) {
|
|
27400
27449
|
const targetHome = opts.targetHome ?? os5.homedir();
|
|
27401
27450
|
const pluginKey = opts.pluginKey ?? DEFAULT_PLUGIN_KEY;
|
|
27451
|
+
const directoryResult = resolveDirectorySourceRoot(targetHome, pluginKey);
|
|
27452
|
+
if (directoryResult !== undefined)
|
|
27453
|
+
return directoryResult;
|
|
27402
27454
|
const registryPath = path9.join(targetHome, ".claude", "plugins", "installed_plugins.json");
|
|
27403
27455
|
let records;
|
|
27404
27456
|
try {
|
|
@@ -27430,6 +27482,7 @@ import fs6 from "fs";
|
|
|
27430
27482
|
import path10 from "path";
|
|
27431
27483
|
import os6 from "os";
|
|
27432
27484
|
import crypto4 from "crypto";
|
|
27485
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
27433
27486
|
function namedError3(name, message) {
|
|
27434
27487
|
const err = new SwitchEngineError(message);
|
|
27435
27488
|
err.name = name;
|
|
@@ -27508,6 +27561,46 @@ function matchesGlob(filename, glob) {
|
|
|
27508
27561
|
const suffix = glob.slice(starIdx + 1);
|
|
27509
27562
|
return filename.length >= prefix.length + suffix.length && filename.startsWith(prefix) && filename.endsWith(suffix);
|
|
27510
27563
|
}
|
|
27564
|
+
function matchingFileNames(dir, glob) {
|
|
27565
|
+
return fs6.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && matchesGlob(e.name, glob)).map((e) => e.name);
|
|
27566
|
+
}
|
|
27567
|
+
function detectGitAvailability(dir) {
|
|
27568
|
+
try {
|
|
27569
|
+
const out = execFileSync2("git", ["-C", dir, "rev-parse", "--is-inside-work-tree"], {
|
|
27570
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
27571
|
+
});
|
|
27572
|
+
return out.toString().trim() === "true" ? "in-repo" : "not-a-repo";
|
|
27573
|
+
} catch (err) {
|
|
27574
|
+
return err.code === "ENOENT" ? "no-git" : "not-a-repo";
|
|
27575
|
+
}
|
|
27576
|
+
}
|
|
27577
|
+
function gitTrackedFileNames(dir, filenames) {
|
|
27578
|
+
if (filenames.length === 0)
|
|
27579
|
+
return new Set;
|
|
27580
|
+
try {
|
|
27581
|
+
const out = execFileSync2("git", ["-C", dir, "ls-files", "--", ...filenames], {
|
|
27582
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
27583
|
+
});
|
|
27584
|
+
return new Set(out.toString().split(`
|
|
27585
|
+
`).map((line) => line.trim()).filter(Boolean));
|
|
27586
|
+
} catch {
|
|
27587
|
+
return new Set;
|
|
27588
|
+
}
|
|
27589
|
+
}
|
|
27590
|
+
function checkTrackedPathGuard(activeDir, filenames) {
|
|
27591
|
+
if (filenames.length === 0 || !fs6.existsSync(activeDir))
|
|
27592
|
+
return GUARD_PASS;
|
|
27593
|
+
const availability = detectGitAvailability(activeDir);
|
|
27594
|
+
if (availability === "no-git")
|
|
27595
|
+
return GUARD_UNCHECKED;
|
|
27596
|
+
if (availability === "not-a-repo")
|
|
27597
|
+
return GUARD_PASS;
|
|
27598
|
+
const tracked = gitTrackedFileNames(activeDir, filenames);
|
|
27599
|
+
if (tracked.size === 0)
|
|
27600
|
+
return GUARD_PASS;
|
|
27601
|
+
const offending = filenames.find((name) => tracked.has(name));
|
|
27602
|
+
return { blocked: true, path: path10.join(activeDir, offending), unchecked: false };
|
|
27603
|
+
}
|
|
27511
27604
|
function assertStateWritable(stateFilePath) {
|
|
27512
27605
|
const dir = path10.dirname(stateFilePath);
|
|
27513
27606
|
try {
|
|
@@ -27637,12 +27730,27 @@ function switchProfile(opts) {
|
|
|
27637
27730
|
rows.push({ host: h.host, status: "switched" });
|
|
27638
27731
|
continue;
|
|
27639
27732
|
}
|
|
27733
|
+
const candidateNames = matchingFileNames(h.variantDir, h.layout.activeGlob);
|
|
27734
|
+
const guard = checkTrackedPathGuard(h.layout.activeDir, candidateNames);
|
|
27735
|
+
if (guard.blocked) {
|
|
27736
|
+
rows.push({
|
|
27737
|
+
host: h.host,
|
|
27738
|
+
status: "failed",
|
|
27739
|
+
reason: `refusing to write ${guard.path} \u2014 it is tracked by git and this switch would dirty the checkout`
|
|
27740
|
+
});
|
|
27741
|
+
continue;
|
|
27742
|
+
}
|
|
27640
27743
|
try {
|
|
27641
27744
|
const filesChanged = copyVariant(h.host, h.layout, h.variantDir);
|
|
27642
27745
|
updatePlatform(stateFilePath, h.host, {
|
|
27643
27746
|
modelProfile: { profile: opts.profile, switchedAt: new Date().toISOString() }
|
|
27644
27747
|
});
|
|
27645
|
-
rows.push({
|
|
27748
|
+
rows.push({
|
|
27749
|
+
host: h.host,
|
|
27750
|
+
status: "switched",
|
|
27751
|
+
filesChanged,
|
|
27752
|
+
...guard.unchecked ? { reason: "tracked-path guard could not verify (git unavailable) \u2014 proceeded unchecked" } : {}
|
|
27753
|
+
});
|
|
27646
27754
|
} catch (err) {
|
|
27647
27755
|
rows.push({ host: h.host, status: "failed", reason: err.message });
|
|
27648
27756
|
}
|
|
@@ -27658,7 +27766,7 @@ function orderRows(universe, rows) {
|
|
|
27658
27766
|
const byHost = new Map(rows.map((r) => [r.host, r]));
|
|
27659
27767
|
return HOSTS.filter((h) => universe.includes(h) && byHost.has(h)).map((h) => byHost.get(h));
|
|
27660
27768
|
}
|
|
27661
|
-
var SwitchEngineError, UnknownProfileError = (profile, known) => namedError3("UnknownProfileError", `unknown profile "${profile}" \u2014 installed: ${known.length > 0 ? known.join(", ") : "none"}`), NoHostsDetectedError = () => namedError3("NoHostsDetectedError", "no installed hosts found");
|
|
27769
|
+
var SwitchEngineError, UnknownProfileError = (profile, known) => namedError3("UnknownProfileError", `unknown profile "${profile}" \u2014 installed: ${known.length > 0 ? known.join(", ") : "none"}`), NoHostsDetectedError = () => namedError3("NoHostsDetectedError", "no installed hosts found"), GUARD_PASS, GUARD_UNCHECKED;
|
|
27662
27770
|
var init_engine = __esm(() => {
|
|
27663
27771
|
init_hosts();
|
|
27664
27772
|
init_state();
|
|
@@ -27670,6 +27778,8 @@ var init_engine = __esm(() => {
|
|
|
27670
27778
|
this.name = "SwitchEngineError";
|
|
27671
27779
|
}
|
|
27672
27780
|
};
|
|
27781
|
+
GUARD_PASS = { blocked: false, unchecked: false };
|
|
27782
|
+
GUARD_UNCHECKED = { blocked: false, unchecked: true };
|
|
27673
27783
|
});
|
|
27674
27784
|
|
|
27675
27785
|
// ../../packages/shared/dist/profile-switch/report.js
|
|
@@ -138783,7 +138893,7 @@ var init_git_ref_validation = __esm(() => {
|
|
|
138783
138893
|
});
|
|
138784
138894
|
|
|
138785
138895
|
// ../../packages/core/dist/services/symbol/impact-analysis.js
|
|
138786
|
-
import { execFileSync as
|
|
138896
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
138787
138897
|
function readBfsCteFlag() {
|
|
138788
138898
|
try {
|
|
138789
138899
|
return Boolean(config2.get("impact")?.bfsCteEnabled);
|
|
@@ -139109,19 +139219,19 @@ function defaultDiffRunner(projectPath2, scope, baseBranch, since) {
|
|
|
139109
139219
|
let diffRange;
|
|
139110
139220
|
if (since) {
|
|
139111
139221
|
try {
|
|
139112
|
-
ref =
|
|
139222
|
+
ref = execFileSync3("git", ["-C", projectPath2, "rev-parse", "--verify", `${since}^{commit}`], {
|
|
139113
139223
|
cwd: projectPath2,
|
|
139114
139224
|
encoding: "utf-8",
|
|
139115
139225
|
stdio: ["ignore", "pipe", "pipe"]
|
|
139116
139226
|
}).trim();
|
|
139117
139227
|
} catch {
|
|
139118
|
-
ref =
|
|
139228
|
+
ref = execFileSync3("git", ["-C", projectPath2, "rev-list", "-1", `--before=${since}`, "HEAD"], {
|
|
139119
139229
|
cwd: projectPath2,
|
|
139120
139230
|
encoding: "utf-8",
|
|
139121
139231
|
stdio: ["ignore", "pipe", "pipe"]
|
|
139122
139232
|
}).trim();
|
|
139123
139233
|
if (!ref) {
|
|
139124
|
-
const emptyTree =
|
|
139234
|
+
const emptyTree = execFileSync3("git", ["hash-object", "-t", "tree", "--stdin"], {
|
|
139125
139235
|
cwd: projectPath2,
|
|
139126
139236
|
encoding: "utf-8",
|
|
139127
139237
|
input: "",
|
|
@@ -139133,7 +139243,7 @@ function defaultDiffRunner(projectPath2, scope, baseBranch, since) {
|
|
|
139133
139243
|
}
|
|
139134
139244
|
args.push(diffRange ?? `${ref}...HEAD`);
|
|
139135
139245
|
}
|
|
139136
|
-
const out =
|
|
139246
|
+
const out = execFileSync3("git", args, {
|
|
139137
139247
|
cwd: projectPath2,
|
|
139138
139248
|
encoding: "utf-8",
|
|
139139
139249
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -139145,7 +139255,7 @@ function defaultDiffRunner(projectPath2, scope, baseBranch, since) {
|
|
|
139145
139255
|
let untrackedFiltered = 0;
|
|
139146
139256
|
const merged = new Set(tracked);
|
|
139147
139257
|
if (includeUntracked) {
|
|
139148
|
-
const untracked =
|
|
139258
|
+
const untracked = execFileSync3("git", ["-C", projectPath2, "ls-files", "--others", "--exclude-standard"], {
|
|
139149
139259
|
cwd: projectPath2,
|
|
139150
139260
|
encoding: "utf-8",
|
|
139151
139261
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -140181,7 +140291,7 @@ var init_tools = __esm(() => {
|
|
|
140181
140291
|
});
|
|
140182
140292
|
|
|
140183
140293
|
// ../../packages/core/dist/services/executor/runtime.js
|
|
140184
|
-
import { execFileSync as
|
|
140294
|
+
import { execFileSync as execFileSync4, execSync } from "child_process";
|
|
140185
140295
|
function commandExists(cmd) {
|
|
140186
140296
|
try {
|
|
140187
140297
|
const check2 = isWindows ? `where ${cmd}` : `command -v ${cmd}`;
|
|
@@ -140214,7 +140324,7 @@ function getVersion(cmd, args = ["--version"], deps) {
|
|
|
140214
140324
|
timeout: 5000
|
|
140215
140325
|
}).trim().split(/\r?\n/)[0];
|
|
140216
140326
|
}
|
|
140217
|
-
return
|
|
140327
|
+
return execFileSync4(cmd, args, {
|
|
140218
140328
|
encoding: "utf-8",
|
|
140219
140329
|
stdio: ["pipe", "pipe", "pipe"],
|
|
140220
140330
|
timeout: 5000
|
|
@@ -140355,12 +140465,12 @@ var init_runtime = __esm(() => {
|
|
|
140355
140465
|
|
|
140356
140466
|
// ../../packages/core/dist/services/executor/sandbox.js
|
|
140357
140467
|
import { realpathSync as realpathSync2 } from "fs";
|
|
140358
|
-
import { execFileSync as
|
|
140468
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
140359
140469
|
function isDockerAvailable() {
|
|
140360
140470
|
if (_dockerAvailable !== null)
|
|
140361
140471
|
return _dockerAvailable;
|
|
140362
140472
|
try {
|
|
140363
|
-
|
|
140473
|
+
execFileSync5("docker", ["--version"], { stdio: "pipe", timeout: 5000 });
|
|
140364
140474
|
_dockerAvailable = true;
|
|
140365
140475
|
} catch {
|
|
140366
140476
|
_dockerAvailable = false;
|
|
@@ -140371,7 +140481,7 @@ function isSeatbeltAvailable() {
|
|
|
140371
140481
|
if (_seatbeltAvailable !== null)
|
|
140372
140482
|
return _seatbeltAvailable;
|
|
140373
140483
|
try {
|
|
140374
|
-
|
|
140484
|
+
execFileSync5("sandbox-exec", ["--version"], { stdio: "pipe", timeout: 5000 });
|
|
140375
140485
|
_seatbeltAvailable = true;
|
|
140376
140486
|
} catch {
|
|
140377
140487
|
_seatbeltAvailable = false;
|
|
@@ -140476,7 +140586,7 @@ var init_sandbox = __esm(() => {
|
|
|
140476
140586
|
});
|
|
140477
140587
|
|
|
140478
140588
|
// ../../packages/core/dist/services/executor/executor.js
|
|
140479
|
-
import { spawn, execSync as execSync2, execFileSync as
|
|
140589
|
+
import { spawn, execSync as execSync2, execFileSync as execFileSync6 } from "child_process";
|
|
140480
140590
|
import { mkdtempSync, writeFileSync as writeFileSync2, rmSync, realpathSync as realpathSync3 } from "fs";
|
|
140481
140591
|
import { join as join3, resolve as resolve5, isAbsolute, relative as relative2 } from "path";
|
|
140482
140592
|
import { tmpdir } from "os";
|
|
@@ -140679,7 +140789,7 @@ ${body}`;
|
|
|
140679
140789
|
const binPath = srcPath.replace(/\.rs$/, isWin ? ".exe" : "");
|
|
140680
140790
|
try {
|
|
140681
140791
|
try {
|
|
140682
|
-
|
|
140792
|
+
execFileSync6("rustc", [srcPath, "-o", binPath], {
|
|
140683
140793
|
cwd,
|
|
140684
140794
|
timeout: Math.min(timeout, 60000),
|
|
140685
140795
|
encoding: "utf-8",
|
|
@@ -143621,6 +143731,85 @@ var init_scheduler = __esm(() => {
|
|
|
143621
143731
|
};
|
|
143622
143732
|
});
|
|
143623
143733
|
|
|
143734
|
+
// ../../packages/core/dist/data/proposal/proposal-payload-validation.js
|
|
143735
|
+
function isRecord(value) {
|
|
143736
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
143737
|
+
}
|
|
143738
|
+
function isStringArray(value) {
|
|
143739
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
143740
|
+
}
|
|
143741
|
+
function isString(value) {
|
|
143742
|
+
return typeof value === "string";
|
|
143743
|
+
}
|
|
143744
|
+
function isNumber(value) {
|
|
143745
|
+
return typeof value === "number";
|
|
143746
|
+
}
|
|
143747
|
+
function isValidProposalPayload(kind, value) {
|
|
143748
|
+
const rule = PROPOSAL_PAYLOAD_RULES[kind];
|
|
143749
|
+
if (!Object.keys(value).every((key) => rule.allowedKeys.includes(key)))
|
|
143750
|
+
return false;
|
|
143751
|
+
if (rule.requireNonEmpty && Object.keys(value).length === 0)
|
|
143752
|
+
return false;
|
|
143753
|
+
for (const [field3, fieldRule] of Object.entries(rule.fields)) {
|
|
143754
|
+
const present = value[field3] !== undefined;
|
|
143755
|
+
if (!present) {
|
|
143756
|
+
if (!fieldRule.optional)
|
|
143757
|
+
return false;
|
|
143758
|
+
continue;
|
|
143759
|
+
}
|
|
143760
|
+
if (!fieldRule.validate(value[field3]))
|
|
143761
|
+
return false;
|
|
143762
|
+
}
|
|
143763
|
+
return true;
|
|
143764
|
+
}
|
|
143765
|
+
function assertValidProposalPayload(kind, value) {
|
|
143766
|
+
if (!isRecord(value)) {
|
|
143767
|
+
throw new ProposalPayloadValidationError(kind, `proposal payload must be an object for kind "${kind}"`);
|
|
143768
|
+
}
|
|
143769
|
+
if (!isValidProposalPayload(kind, value)) {
|
|
143770
|
+
throw new ProposalPayloadValidationError(kind, `proposal payload is invalid for kind "${kind}"`);
|
|
143771
|
+
}
|
|
143772
|
+
}
|
|
143773
|
+
var PROPOSAL_PAYLOAD_RULES, ProposalPayloadValidationError;
|
|
143774
|
+
var init_proposal_payload_validation = __esm(() => {
|
|
143775
|
+
PROPOSAL_PAYLOAD_RULES = {
|
|
143776
|
+
"memory.create": {
|
|
143777
|
+
allowedKeys: ["content", "type", "level", "importance", "tags"],
|
|
143778
|
+
fields: {
|
|
143779
|
+
content: { optional: false, validate: isString },
|
|
143780
|
+
type: { optional: true, validate: isString },
|
|
143781
|
+
level: { optional: true, validate: isNumber },
|
|
143782
|
+
importance: { optional: true, validate: isNumber },
|
|
143783
|
+
tags: { optional: true, validate: isStringArray }
|
|
143784
|
+
}
|
|
143785
|
+
},
|
|
143786
|
+
"memory.update": {
|
|
143787
|
+
allowedKeys: ["content", "importance", "tags"],
|
|
143788
|
+
requireNonEmpty: true,
|
|
143789
|
+
fields: {
|
|
143790
|
+
content: { optional: true, validate: isString },
|
|
143791
|
+
importance: { optional: true, validate: isNumber },
|
|
143792
|
+
tags: { optional: true, validate: isStringArray }
|
|
143793
|
+
}
|
|
143794
|
+
},
|
|
143795
|
+
"memory.tag": {
|
|
143796
|
+
allowedKeys: ["tags"],
|
|
143797
|
+
fields: {
|
|
143798
|
+
tags: { optional: false, validate: isStringArray }
|
|
143799
|
+
}
|
|
143800
|
+
}
|
|
143801
|
+
};
|
|
143802
|
+
ProposalPayloadValidationError = class ProposalPayloadValidationError extends Error {
|
|
143803
|
+
kind;
|
|
143804
|
+
statusCode = 400;
|
|
143805
|
+
constructor(kind, message) {
|
|
143806
|
+
super(message);
|
|
143807
|
+
this.kind = kind;
|
|
143808
|
+
this.name = "ProposalPayloadValidationError";
|
|
143809
|
+
}
|
|
143810
|
+
};
|
|
143811
|
+
});
|
|
143812
|
+
|
|
143624
143813
|
// ../../packages/core/dist/data/proposal/proposal-contract.js
|
|
143625
143814
|
class MemoryProposalStore {
|
|
143626
143815
|
rows = [];
|
|
@@ -143644,26 +143833,53 @@ class MemoryProposalStore {
|
|
|
143644
143833
|
row.decidedAt = decidedAt ?? Date.now();
|
|
143645
143834
|
return structuredClone(row);
|
|
143646
143835
|
}
|
|
143836
|
+
async create(input) {
|
|
143837
|
+
assertValidProposalPayload(input.kind, input.payload);
|
|
143838
|
+
const record3 = {
|
|
143839
|
+
id: input.id,
|
|
143840
|
+
projectId: input.projectId,
|
|
143841
|
+
kind: input.kind,
|
|
143842
|
+
targetMemoryId: input.targetMemoryId ?? null,
|
|
143843
|
+
payload: input.payload,
|
|
143844
|
+
rationale: input.rationale ?? "",
|
|
143845
|
+
status: "pending",
|
|
143846
|
+
createdAt: input.createdAt ?? Date.now(),
|
|
143847
|
+
decidedAt: null
|
|
143848
|
+
};
|
|
143849
|
+
this.rows.push(structuredClone(record3));
|
|
143850
|
+
return structuredClone(record3);
|
|
143851
|
+
}
|
|
143852
|
+
async update(id, patch) {
|
|
143853
|
+
const row = this.rows.find((item) => item.id === id);
|
|
143854
|
+
if (!row)
|
|
143855
|
+
return null;
|
|
143856
|
+
if (patch.payload !== undefined)
|
|
143857
|
+
assertValidProposalPayload(row.kind, patch.payload);
|
|
143858
|
+
if (patch.rationale !== undefined)
|
|
143859
|
+
row.rationale = patch.rationale;
|
|
143860
|
+
if (patch.payload !== undefined)
|
|
143861
|
+
row.payload = patch.payload;
|
|
143862
|
+
return structuredClone(row);
|
|
143863
|
+
}
|
|
143864
|
+
async delete(id) {
|
|
143865
|
+
const index = this.rows.findIndex((item) => item.id === id);
|
|
143866
|
+
if (index === -1)
|
|
143867
|
+
return null;
|
|
143868
|
+
this.rows.splice(index, 1);
|
|
143869
|
+
return id;
|
|
143870
|
+
}
|
|
143647
143871
|
async journalMode() {
|
|
143648
143872
|
return "memory";
|
|
143649
143873
|
}
|
|
143650
143874
|
}
|
|
143651
143875
|
var PROPOSAL_STATUSES, PROPOSAL_KINDS;
|
|
143652
143876
|
var init_proposal_contract = __esm(() => {
|
|
143877
|
+
init_proposal_payload_validation();
|
|
143653
143878
|
PROPOSAL_STATUSES = ["pending", "approved", "rejected"];
|
|
143654
143879
|
PROPOSAL_KINDS = ["memory.create", "memory.update", "memory.tag"];
|
|
143655
143880
|
});
|
|
143656
143881
|
|
|
143657
143882
|
// ../../packages/core/dist/data/proposal/proposal-repository-pg.js
|
|
143658
|
-
function isRecord(value) {
|
|
143659
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
143660
|
-
}
|
|
143661
|
-
function stringArray(value) {
|
|
143662
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
143663
|
-
}
|
|
143664
|
-
function validKeys(value, allowed) {
|
|
143665
|
-
return Object.keys(value).every((key) => allowed.includes(key));
|
|
143666
|
-
}
|
|
143667
143883
|
function parsePayload(raw2, kind) {
|
|
143668
143884
|
let value;
|
|
143669
143885
|
try {
|
|
@@ -143674,15 +143890,7 @@ function parsePayload(raw2, kind) {
|
|
|
143674
143890
|
if (!isRecord(value)) {
|
|
143675
143891
|
throw storeCorruption("proposal.payload_json", new TypeError("expected object"));
|
|
143676
143892
|
}
|
|
143677
|
-
|
|
143678
|
-
if (kind === "memory.create") {
|
|
143679
|
-
valid = validKeys(value, ["content", "type", "level", "importance", "tags"]) && typeof value.content === "string" && (value.type === undefined || typeof value.type === "string") && (value.level === undefined || typeof value.level === "number") && (value.importance === undefined || typeof value.importance === "number") && (value.tags === undefined || stringArray(value.tags));
|
|
143680
|
-
} else if (kind === "memory.update") {
|
|
143681
|
-
valid = validKeys(value, ["content", "importance", "tags"]) && Object.keys(value).length > 0 && (value.content === undefined || typeof value.content === "string") && (value.importance === undefined || typeof value.importance === "number") && (value.tags === undefined || stringArray(value.tags));
|
|
143682
|
-
} else {
|
|
143683
|
-
valid = validKeys(value, ["tags"]) && stringArray(value.tags);
|
|
143684
|
-
}
|
|
143685
|
-
if (!valid) {
|
|
143893
|
+
if (!isValidProposalPayload(kind, value)) {
|
|
143686
143894
|
throw storeCorruption("proposal.payload_json", new TypeError("invalid proposal payload"));
|
|
143687
143895
|
}
|
|
143688
143896
|
return value;
|
|
@@ -143816,6 +144024,65 @@ class PgProposalStore {
|
|
|
143816
144024
|
this.mirror.set(id, persisted);
|
|
143817
144025
|
return structuredClone(persisted);
|
|
143818
144026
|
}
|
|
144027
|
+
async create(input) {
|
|
144028
|
+
assertValidProposalPayload(input.kind, input.payload);
|
|
144029
|
+
const record3 = {
|
|
144030
|
+
id: input.id,
|
|
144031
|
+
projectId: input.projectId,
|
|
144032
|
+
kind: input.kind,
|
|
144033
|
+
targetMemoryId: input.targetMemoryId ?? null,
|
|
144034
|
+
payload: input.payload,
|
|
144035
|
+
rationale: input.rationale ?? "",
|
|
144036
|
+
status: "pending",
|
|
144037
|
+
createdAt: input.createdAt ?? Date.now(),
|
|
144038
|
+
decidedAt: null
|
|
144039
|
+
};
|
|
144040
|
+
await this.insert(record3);
|
|
144041
|
+
return record3;
|
|
144042
|
+
}
|
|
144043
|
+
async update(id, patch) {
|
|
144044
|
+
await this.ensureHydrated();
|
|
144045
|
+
const current = this.mirror.get(id);
|
|
144046
|
+
if (!current)
|
|
144047
|
+
return null;
|
|
144048
|
+
if (patch.payload !== undefined) {
|
|
144049
|
+
assertValidProposalPayload(current.kind, patch.payload);
|
|
144050
|
+
}
|
|
144051
|
+
const merged = {
|
|
144052
|
+
...current,
|
|
144053
|
+
...patch.rationale !== undefined ? { rationale: patch.rationale } : {},
|
|
144054
|
+
...patch.payload !== undefined ? { payload: patch.payload } : {}
|
|
144055
|
+
};
|
|
144056
|
+
let rows;
|
|
144057
|
+
try {
|
|
144058
|
+
rows = await this.getClient().$queryRaw`
|
|
144059
|
+
UPDATE proposals
|
|
144060
|
+
SET rationale = ${merged.rationale}, payload_json = ${JSON.stringify(merged.payload)}
|
|
144061
|
+
WHERE id = ${id}
|
|
144062
|
+
RETURNING id, project_id, kind, target_memory_id, payload_json,
|
|
144063
|
+
rationale, status, created_at, decided_at`;
|
|
144064
|
+
} catch (error51) {
|
|
144065
|
+
throw searchBackendUnavailable("proposal_store", error51);
|
|
144066
|
+
}
|
|
144067
|
+
if (!rows[0])
|
|
144068
|
+
return null;
|
|
144069
|
+
const persisted = toRecord(rows[0]);
|
|
144070
|
+
this.mirror.set(id, persisted);
|
|
144071
|
+
return structuredClone(persisted);
|
|
144072
|
+
}
|
|
144073
|
+
async delete(id) {
|
|
144074
|
+
await this.ensureHydrated();
|
|
144075
|
+
let affected;
|
|
144076
|
+
try {
|
|
144077
|
+
affected = await this.getClient().$executeRaw`DELETE FROM proposals WHERE id = ${id}`;
|
|
144078
|
+
} catch (error51) {
|
|
144079
|
+
throw searchBackendUnavailable("proposal_store", error51);
|
|
144080
|
+
}
|
|
144081
|
+
if (affected === 0)
|
|
144082
|
+
return null;
|
|
144083
|
+
this.mirror.delete(id);
|
|
144084
|
+
return id;
|
|
144085
|
+
}
|
|
143819
144086
|
async journalMode() {
|
|
143820
144087
|
await this.ensureHydrated();
|
|
143821
144088
|
return "postgres";
|
|
@@ -143832,6 +144099,7 @@ var init_proposal_repository_pg = __esm(() => {
|
|
|
143832
144099
|
init_alias_resolver();
|
|
143833
144100
|
init_search_diagnostics();
|
|
143834
144101
|
init_proposal_contract();
|
|
144102
|
+
init_proposal_payload_validation();
|
|
143835
144103
|
});
|
|
143836
144104
|
|
|
143837
144105
|
// ../../packages/core/dist/data/proposal/proposal-repository.js
|
|
@@ -164234,6 +164502,29 @@ class MemoryHandoffStore {
|
|
|
164234
164502
|
row.acceptedAt = status === "accepted" ? acceptedAt ?? Date.now() : null;
|
|
164235
164503
|
return structuredClone(row);
|
|
164236
164504
|
}
|
|
164505
|
+
async update(id, patch) {
|
|
164506
|
+
const row = this.rows.find((item) => item.id === id);
|
|
164507
|
+
if (!row)
|
|
164508
|
+
return null;
|
|
164509
|
+
if (patch.targetAgent !== undefined)
|
|
164510
|
+
row.targetAgent = patch.targetAgent;
|
|
164511
|
+
if (patch.summary !== undefined)
|
|
164512
|
+
row.summary = patch.summary;
|
|
164513
|
+
if (patch.openQuestions !== undefined)
|
|
164514
|
+
row.openQuestions = patch.openQuestions;
|
|
164515
|
+
if (patch.nextSteps !== undefined)
|
|
164516
|
+
row.nextSteps = patch.nextSteps;
|
|
164517
|
+
if (patch.files !== undefined)
|
|
164518
|
+
row.files = patch.files;
|
|
164519
|
+
return structuredClone(row);
|
|
164520
|
+
}
|
|
164521
|
+
async delete(id) {
|
|
164522
|
+
const index = this.rows.findIndex((item) => item.id === id);
|
|
164523
|
+
if (index === -1)
|
|
164524
|
+
return null;
|
|
164525
|
+
this.rows.splice(index, 1);
|
|
164526
|
+
return id;
|
|
164527
|
+
}
|
|
164237
164528
|
async journalMode() {
|
|
164238
164529
|
return "memory";
|
|
164239
164530
|
}
|
|
@@ -164389,6 +164680,53 @@ class PgHandoffStore {
|
|
|
164389
164680
|
this.mirror.set(id, persisted);
|
|
164390
164681
|
return structuredClone(persisted);
|
|
164391
164682
|
}
|
|
164683
|
+
async update(id, patch) {
|
|
164684
|
+
await this.ensureHydrated();
|
|
164685
|
+
const current = this.mirror.get(id);
|
|
164686
|
+
if (!current)
|
|
164687
|
+
return null;
|
|
164688
|
+
const merged = {
|
|
164689
|
+
...current,
|
|
164690
|
+
...patch.targetAgent !== undefined ? { targetAgent: patch.targetAgent } : {},
|
|
164691
|
+
...patch.summary !== undefined ? { summary: patch.summary } : {},
|
|
164692
|
+
...patch.openQuestions !== undefined ? { openQuestions: patch.openQuestions } : {},
|
|
164693
|
+
...patch.nextSteps !== undefined ? { nextSteps: patch.nextSteps } : {},
|
|
164694
|
+
...patch.files !== undefined ? { files: patch.files } : {}
|
|
164695
|
+
};
|
|
164696
|
+
let rows;
|
|
164697
|
+
try {
|
|
164698
|
+
rows = await this.getClient().$queryRaw`
|
|
164699
|
+
UPDATE handoffs
|
|
164700
|
+
SET target_agent = ${merged.targetAgent}, summary = ${merged.summary},
|
|
164701
|
+
open_questions_json = ${JSON.stringify(merged.openQuestions ?? [])},
|
|
164702
|
+
next_steps_json = ${JSON.stringify(merged.nextSteps ?? [])},
|
|
164703
|
+
files_json = ${JSON.stringify(merged.files ?? [])}
|
|
164704
|
+
WHERE id = ${id}
|
|
164705
|
+
RETURNING id, project_id, source_session_id, target_agent, summary,
|
|
164706
|
+
open_questions_json, next_steps_json, files_json, status,
|
|
164707
|
+
created_at, accepted_at`;
|
|
164708
|
+
} catch (error51) {
|
|
164709
|
+
throw searchBackendUnavailable("handoff_store", error51);
|
|
164710
|
+
}
|
|
164711
|
+
if (!rows[0])
|
|
164712
|
+
return null;
|
|
164713
|
+
const persisted = toRecord2(rows[0]);
|
|
164714
|
+
this.mirror.set(id, persisted);
|
|
164715
|
+
return structuredClone(persisted);
|
|
164716
|
+
}
|
|
164717
|
+
async delete(id) {
|
|
164718
|
+
await this.ensureHydrated();
|
|
164719
|
+
let affected;
|
|
164720
|
+
try {
|
|
164721
|
+
affected = await this.getClient().$executeRaw`DELETE FROM handoffs WHERE id = ${id}`;
|
|
164722
|
+
} catch (error51) {
|
|
164723
|
+
throw searchBackendUnavailable("handoff_store", error51);
|
|
164724
|
+
}
|
|
164725
|
+
if (affected === 0)
|
|
164726
|
+
return null;
|
|
164727
|
+
this.mirror.delete(id);
|
|
164728
|
+
return id;
|
|
164729
|
+
}
|
|
164392
164730
|
async journalMode() {
|
|
164393
164731
|
await this.ensureHydrated();
|
|
164394
164732
|
return "postgres";
|
|
@@ -164427,8 +164765,6 @@ var init_handoff_repository = __esm(() => {
|
|
|
164427
164765
|
});
|
|
164428
164766
|
|
|
164429
164767
|
// ../../packages/core/dist/services/handoff/handoff-service.js
|
|
164430
|
-
import { randomUUID as randomUUID10 } from "crypto";
|
|
164431
|
-
|
|
164432
164768
|
class HandoffService {
|
|
164433
164769
|
store;
|
|
164434
164770
|
memoryRepo;
|
|
@@ -164440,7 +164776,8 @@ class HandoffService {
|
|
|
164440
164776
|
this.idFactory = deps.idFactory ?? (() => newHandoffId());
|
|
164441
164777
|
const injectedRepo = deps.memoryRepo;
|
|
164442
164778
|
this.memoryRepo = injectedRepo ?? {
|
|
164443
|
-
insert: (i) => getMemoryRepository().insert(i)
|
|
164779
|
+
insert: (i) => getMemoryRepository().insert(i),
|
|
164780
|
+
update: (id, p) => getMemoryRepository().update(id, p)
|
|
164444
164781
|
};
|
|
164445
164782
|
}
|
|
164446
164783
|
async begin(input) {
|
|
@@ -164482,7 +164819,7 @@ class HandoffService {
|
|
|
164482
164819
|
return { ok: true, id, status: "open", memoryId };
|
|
164483
164820
|
}
|
|
164484
164821
|
async dualWrite(record3) {
|
|
164485
|
-
const memId =
|
|
164822
|
+
const memId = dualWriteMemoryId(record3.id);
|
|
164486
164823
|
const input = buildHandoffMemoryInput(memId, record3);
|
|
164487
164824
|
await Promise.resolve(this.memoryRepo.insert(input));
|
|
164488
164825
|
return memId;
|
|
@@ -164526,6 +164863,50 @@ class HandoffService {
|
|
|
164526
164863
|
async listPending(projectId, targetAgent) {
|
|
164527
164864
|
return this.store.listPending(projectId, targetAgent ?? undefined);
|
|
164528
164865
|
}
|
|
164866
|
+
async update(params) {
|
|
164867
|
+
if (!params || !params.id) {
|
|
164868
|
+
return { ok: false, reason: "missing-id" };
|
|
164869
|
+
}
|
|
164870
|
+
const row = await this.store.getById(params.id);
|
|
164871
|
+
if (!row)
|
|
164872
|
+
return { ok: false, reason: "not-found" };
|
|
164873
|
+
if (params.projectId && row.projectId !== params.projectId) {
|
|
164874
|
+
return { ok: false, reason: "project-mismatch" };
|
|
164875
|
+
}
|
|
164876
|
+
const updated = await this.store.update(params.id, params.patch);
|
|
164877
|
+
if (!updated)
|
|
164878
|
+
return { ok: false, reason: "not-found" };
|
|
164879
|
+
const touchesMemoryContent = params.patch.summary !== undefined || params.patch.openQuestions !== undefined || params.patch.nextSteps !== undefined || params.patch.files !== undefined;
|
|
164880
|
+
if (touchesMemoryContent) {
|
|
164881
|
+
await this.refreshDualWriteMemory(updated);
|
|
164882
|
+
}
|
|
164883
|
+
return { ok: true, handoff: updated };
|
|
164884
|
+
}
|
|
164885
|
+
async refreshDualWriteMemory(record3) {
|
|
164886
|
+
try {
|
|
164887
|
+
const memId = dualWriteMemoryId(record3.id);
|
|
164888
|
+
const content = formatMemoryContent(record3);
|
|
164889
|
+
await Promise.resolve(this.memoryRepo.update(memId, { content }));
|
|
164890
|
+
} catch {}
|
|
164891
|
+
}
|
|
164892
|
+
async delete(params) {
|
|
164893
|
+
if (!params || !params.id) {
|
|
164894
|
+
return { ok: false, reason: "missing-id" };
|
|
164895
|
+
}
|
|
164896
|
+
const row = await this.store.getById(params.id);
|
|
164897
|
+
if (!row)
|
|
164898
|
+
return { ok: false, reason: "not-found" };
|
|
164899
|
+
if (params.projectId && row.projectId !== params.projectId) {
|
|
164900
|
+
return { ok: false, reason: "project-mismatch" };
|
|
164901
|
+
}
|
|
164902
|
+
const deletedId = await this.store.delete(params.id);
|
|
164903
|
+
if (!deletedId)
|
|
164904
|
+
return { ok: false, reason: "not-found" };
|
|
164905
|
+
return { ok: true, id: deletedId };
|
|
164906
|
+
}
|
|
164907
|
+
}
|
|
164908
|
+
function dualWriteMemoryId(handoffId) {
|
|
164909
|
+
return `handoff-mem-${handoffId}`;
|
|
164529
164910
|
}
|
|
164530
164911
|
function buildHandoffMemoryInput(memId, record3) {
|
|
164531
164912
|
const content = formatMemoryContent(record3);
|
|
@@ -164739,6 +165120,7 @@ __export(exports_dist, {
|
|
|
164739
165120
|
newObservationId: () => newObservationId,
|
|
164740
165121
|
newHandoffId: () => newHandoffId,
|
|
164741
165122
|
jsonToKeyPathChunks: () => jsonToKeyPathChunks,
|
|
165123
|
+
isValidProposalPayload: () => isValidProposalPayload,
|
|
164742
165124
|
isKnownRegistryTable: () => isKnownRegistryTable,
|
|
164743
165125
|
intentSearch: () => intentSearch,
|
|
164744
165126
|
installProjectIdentityGuardsFromPool: () => installProjectIdentityGuardsFromPool,
|
|
@@ -164831,6 +165213,7 @@ __export(exports_dist, {
|
|
|
164831
165213
|
boundDiagnostics: () => boundDiagnostics,
|
|
164832
165214
|
bootstrapService: () => bootstrapService,
|
|
164833
165215
|
autoImproveJob: () => autoImproveJob,
|
|
165216
|
+
assertValidProposalPayload: () => assertValidProposalPayload,
|
|
164834
165217
|
assertUrlSafe: () => assertUrlSafe,
|
|
164835
165218
|
assertParserReadyForIndexing: () => assertParserReadyForIndexing,
|
|
164836
165219
|
assertGenerationNotStale: () => assertGenerationNotStale,
|
|
@@ -164895,6 +165278,7 @@ __export(exports_dist, {
|
|
|
164895
165278
|
RUBY_QUERY_PACK: () => RUBY_QUERY_PACK,
|
|
164896
165279
|
REFERENCE_TOKEN_COST: () => REFERENCE_TOKEN_COST,
|
|
164897
165280
|
QueueSaturatedError: () => QueueSaturatedError,
|
|
165281
|
+
ProposalPayloadValidationError: () => ProposalPayloadValidationError,
|
|
164898
165282
|
ProposalEnrichmentSchema: () => ProposalEnrichmentSchema,
|
|
164899
165283
|
ProjectIdentityPreviewRequestSchema: () => ProjectIdentityPreviewRequestSchema,
|
|
164900
165284
|
ProjectIdentityPreviewPlanner: () => ProjectIdentityPreviewPlanner,
|
|
@@ -164910,6 +165294,7 @@ __export(exports_dist, {
|
|
|
164910
165294
|
ParserAcquireTimeoutError: () => ParserAcquireTimeoutError,
|
|
164911
165295
|
PYTHON_QUERY_PACK: () => PYTHON_QUERY_PACK,
|
|
164912
165296
|
PROPOSAL_STATUSES: () => PROPOSAL_STATUSES,
|
|
165297
|
+
PROPOSAL_PAYLOAD_RULES: () => PROPOSAL_PAYLOAD_RULES,
|
|
164913
165298
|
PROPOSAL_KINDS: () => PROPOSAL_KINDS,
|
|
164914
165299
|
PROJECT_IDENTITY_REGISTRY_VERSION: () => PROJECT_IDENTITY_REGISTRY_VERSION,
|
|
164915
165300
|
PROJECT_IDENTITY_PLAN_VERSION: () => PROJECT_IDENTITY_PLAN_VERSION,
|
|
@@ -165025,6 +165410,7 @@ var init_dist15 = __esm(() => {
|
|
|
165025
165410
|
init_handoff_service();
|
|
165026
165411
|
init_handoff_auto_injector();
|
|
165027
165412
|
init_proposal_repository();
|
|
165413
|
+
init_proposal_payload_validation();
|
|
165028
165414
|
init_auto_improve_job();
|
|
165029
165415
|
init_tools();
|
|
165030
165416
|
init_services();
|
|
@@ -167667,6 +168053,26 @@ function boundedInt(val, def, min, max) {
|
|
|
167667
168053
|
const i = Math.trunc(n);
|
|
167668
168054
|
return Math.max(min, Math.min(max, i));
|
|
167669
168055
|
}
|
|
168056
|
+
var HANDOFF_PATCH_ALLOWED_FIELDS = [
|
|
168057
|
+
"targetAgent",
|
|
168058
|
+
"summary",
|
|
168059
|
+
"openQuestions",
|
|
168060
|
+
"nextSteps",
|
|
168061
|
+
"files"
|
|
168062
|
+
];
|
|
168063
|
+
var PROPOSAL_PATCH_ALLOWED_FIELDS = ["rationale", "payload"];
|
|
168064
|
+
function isStringArray2(value) {
|
|
168065
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
168066
|
+
}
|
|
168067
|
+
function isRecord2(value) {
|
|
168068
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
168069
|
+
}
|
|
168070
|
+
function isProposalKind(value) {
|
|
168071
|
+
return typeof value === "string" && PROPOSAL_KINDS.includes(value);
|
|
168072
|
+
}
|
|
168073
|
+
function proposalPayloadValidationStatus(error51) {
|
|
168074
|
+
return error51 instanceof ProposalPayloadValidationError ? error51.statusCode : null;
|
|
168075
|
+
}
|
|
167670
168076
|
function serializeSession(s) {
|
|
167671
168077
|
if (!s)
|
|
167672
168078
|
return null;
|
|
@@ -167921,6 +168327,8 @@ class EmbeddedApiClient {
|
|
|
167921
168327
|
return await this.handleProposalApprove(body);
|
|
167922
168328
|
case "/api/v1/proposal/reject":
|
|
167923
168329
|
return await this.handleProposalReject(body);
|
|
168330
|
+
case "/api/v1/proposal/create":
|
|
168331
|
+
return await this.handleProposalCreate(body);
|
|
167924
168332
|
case "/api/v1/synapse/session":
|
|
167925
168333
|
return await this.handleSynapseSession(body);
|
|
167926
168334
|
case "/api/v1/profiles/switch":
|
|
@@ -167983,6 +168391,14 @@ class EmbeddedApiClient {
|
|
|
167983
168391
|
return { success: false, error: "Session not found or expired" };
|
|
167984
168392
|
return { success: true, data: serializeSession(updated) };
|
|
167985
168393
|
}
|
|
168394
|
+
const handoffMatch = endpoint.match(/^\/api\/v1\/handoff\/([^/]+)$/);
|
|
168395
|
+
if (handoffMatch) {
|
|
168396
|
+
return await this.handleHandoffUpdate(handoffMatch[1], body);
|
|
168397
|
+
}
|
|
168398
|
+
const proposalMatch = endpoint.match(/^\/api\/v1\/proposal\/([^/]+)$/);
|
|
168399
|
+
if (proposalMatch) {
|
|
168400
|
+
return await this.handleProposalUpdate(proposalMatch[1], body);
|
|
168401
|
+
}
|
|
167986
168402
|
return httpError(404, `EmbeddedApiClient: no PATCH handler for ${endpoint}`);
|
|
167987
168403
|
} catch (error51) {
|
|
167988
168404
|
if (error51 instanceof ApiHttpError)
|
|
@@ -168003,6 +168419,14 @@ class EmbeddedApiClient {
|
|
|
168003
168419
|
await workspaceManager.removeWorkspace(wsMatch[1]);
|
|
168004
168420
|
return { success: true, data: { removed: wsMatch[1] } };
|
|
168005
168421
|
}
|
|
168422
|
+
const handoffMatch = endpoint.match(/^\/api\/v1\/handoff\/([^/]+)$/);
|
|
168423
|
+
if (handoffMatch) {
|
|
168424
|
+
return await this.handleHandoffDelete(handoffMatch[1], _body);
|
|
168425
|
+
}
|
|
168426
|
+
const proposalMatch = endpoint.match(/^\/api\/v1\/proposal\/([^/]+)$/);
|
|
168427
|
+
if (proposalMatch) {
|
|
168428
|
+
return await this.handleProposalDelete(proposalMatch[1], _body);
|
|
168429
|
+
}
|
|
168006
168430
|
return httpError(404, `EmbeddedApiClient: no DELETE handler for ${endpoint}`);
|
|
168007
168431
|
} catch (error51) {
|
|
168008
168432
|
if (error51 instanceof ApiHttpError)
|
|
@@ -168238,6 +168662,61 @@ class EmbeddedApiClient {
|
|
|
168238
168662
|
throw httpError(500, `handoff list failed: ${e.message}`);
|
|
168239
168663
|
}
|
|
168240
168664
|
}
|
|
168665
|
+
async handleHandoffUpdate(id, body) {
|
|
168666
|
+
const raw2 = body ?? {};
|
|
168667
|
+
const { projectId, ...patchFields } = raw2;
|
|
168668
|
+
const keys = Object.keys(patchFields);
|
|
168669
|
+
const rejected = keys.filter((k) => !HANDOFF_PATCH_ALLOWED_FIELDS.includes(k));
|
|
168670
|
+
if (rejected.length > 0) {
|
|
168671
|
+
throw httpError(400, `field not allowed: ${rejected.join(", ")}`);
|
|
168672
|
+
}
|
|
168673
|
+
const patch = {};
|
|
168674
|
+
if ("targetAgent" in patchFields) {
|
|
168675
|
+
const v = patchFields.targetAgent;
|
|
168676
|
+
if (v !== null && typeof v !== "string")
|
|
168677
|
+
throw httpError(400, "targetAgent must be a string or null");
|
|
168678
|
+
patch.targetAgent = v;
|
|
168679
|
+
}
|
|
168680
|
+
if ("summary" in patchFields) {
|
|
168681
|
+
if (typeof patchFields.summary !== "string")
|
|
168682
|
+
throw httpError(400, "summary must be a string");
|
|
168683
|
+
patch.summary = patchFields.summary;
|
|
168684
|
+
}
|
|
168685
|
+
if ("openQuestions" in patchFields) {
|
|
168686
|
+
if (!isStringArray2(patchFields.openQuestions))
|
|
168687
|
+
throw httpError(400, "openQuestions must be an array of strings");
|
|
168688
|
+
patch.openQuestions = patchFields.openQuestions;
|
|
168689
|
+
}
|
|
168690
|
+
if ("nextSteps" in patchFields) {
|
|
168691
|
+
if (!isStringArray2(patchFields.nextSteps))
|
|
168692
|
+
throw httpError(400, "nextSteps must be an array of strings");
|
|
168693
|
+
patch.nextSteps = patchFields.nextSteps;
|
|
168694
|
+
}
|
|
168695
|
+
if ("files" in patchFields) {
|
|
168696
|
+
if (!isStringArray2(patchFields.files))
|
|
168697
|
+
throw httpError(400, "files must be an array of strings");
|
|
168698
|
+
patch.files = patchFields.files;
|
|
168699
|
+
}
|
|
168700
|
+
if (Object.keys(patch).length === 0)
|
|
168701
|
+
throw httpError(400, "no editable field provided");
|
|
168702
|
+
const result = await getHandoffService().update({
|
|
168703
|
+
id,
|
|
168704
|
+
projectId,
|
|
168705
|
+
patch
|
|
168706
|
+
});
|
|
168707
|
+
if (!result.ok) {
|
|
168708
|
+
const status = result.reason === "not-found" || result.reason === "project-mismatch" ? 404 : 400;
|
|
168709
|
+
throw httpError(status, result.reason ?? "update failed");
|
|
168710
|
+
}
|
|
168711
|
+
return { success: true, data: result.handoff };
|
|
168712
|
+
}
|
|
168713
|
+
async handleHandoffDelete(id, body) {
|
|
168714
|
+
const projectId = body?.projectId;
|
|
168715
|
+
const result = await getHandoffService().delete({ id, projectId });
|
|
168716
|
+
if (!result.ok)
|
|
168717
|
+
throw httpError(404, result.reason ?? "not-found");
|
|
168718
|
+
return { success: true, data: { id: result.id } };
|
|
168719
|
+
}
|
|
168241
168720
|
async handleProposalList(body) {
|
|
168242
168721
|
const { projectId } = body;
|
|
168243
168722
|
if (!projectId || !String(projectId).trim()) {
|
|
@@ -168274,6 +168753,104 @@ class EmbeddedApiClient {
|
|
|
168274
168753
|
throw httpError(500, `proposal reject failed: ${e.message}`);
|
|
168275
168754
|
}
|
|
168276
168755
|
}
|
|
168756
|
+
async handleProposalCreate(body) {
|
|
168757
|
+
const raw2 = body ?? {};
|
|
168758
|
+
if (typeof raw2.projectId !== "string" || !raw2.projectId.trim()) {
|
|
168759
|
+
throw httpError(400, "projectId required");
|
|
168760
|
+
}
|
|
168761
|
+
if (!isProposalKind(raw2.kind)) {
|
|
168762
|
+
throw httpError(400, `kind must be one of ${PROPOSAL_KINDS.join(", ")}`);
|
|
168763
|
+
}
|
|
168764
|
+
if (!isRecord2(raw2.payload)) {
|
|
168765
|
+
throw httpError(400, "payload must be an object");
|
|
168766
|
+
}
|
|
168767
|
+
if (raw2.rationale !== undefined && typeof raw2.rationale !== "string") {
|
|
168768
|
+
throw httpError(400, "rationale must be a string");
|
|
168769
|
+
}
|
|
168770
|
+
if (raw2.targetMemoryId !== undefined && raw2.targetMemoryId !== null && typeof raw2.targetMemoryId !== "string") {
|
|
168771
|
+
throw httpError(400, "targetMemoryId must be a string or null");
|
|
168772
|
+
}
|
|
168773
|
+
const projectId = raw2.projectId;
|
|
168774
|
+
const kind = raw2.kind;
|
|
168775
|
+
const payload = raw2.payload;
|
|
168776
|
+
try {
|
|
168777
|
+
const record3 = await getProposalStore().create({
|
|
168778
|
+
id: newProposalId(),
|
|
168779
|
+
projectId,
|
|
168780
|
+
kind,
|
|
168781
|
+
payload,
|
|
168782
|
+
rationale: raw2.rationale,
|
|
168783
|
+
targetMemoryId: raw2.targetMemoryId
|
|
168784
|
+
});
|
|
168785
|
+
return { success: true, data: record3 };
|
|
168786
|
+
} catch (e) {
|
|
168787
|
+
const validationStatus = proposalPayloadValidationStatus(e);
|
|
168788
|
+
if (validationStatus !== null)
|
|
168789
|
+
throw httpError(validationStatus, e.message);
|
|
168790
|
+
throw e;
|
|
168791
|
+
}
|
|
168792
|
+
}
|
|
168793
|
+
async handleProposalUpdate(id, body) {
|
|
168794
|
+
const raw2 = body ?? {};
|
|
168795
|
+
const { projectId, ...patchFields } = raw2;
|
|
168796
|
+
const keys = Object.keys(patchFields);
|
|
168797
|
+
const rejected = keys.filter((k) => !PROPOSAL_PATCH_ALLOWED_FIELDS.includes(k));
|
|
168798
|
+
if (rejected.length > 0) {
|
|
168799
|
+
throw httpError(400, `field not allowed: ${rejected.join(", ")}`);
|
|
168800
|
+
}
|
|
168801
|
+
const patch = {};
|
|
168802
|
+
if ("rationale" in patchFields) {
|
|
168803
|
+
if (typeof patchFields.rationale !== "string")
|
|
168804
|
+
throw httpError(400, "rationale must be a string");
|
|
168805
|
+
patch.rationale = patchFields.rationale;
|
|
168806
|
+
}
|
|
168807
|
+
if ("payload" in patchFields) {
|
|
168808
|
+
if (!isRecord2(patchFields.payload))
|
|
168809
|
+
throw httpError(400, "payload must be an object");
|
|
168810
|
+
patch.payload = patchFields.payload;
|
|
168811
|
+
}
|
|
168812
|
+
if (Object.keys(patch).length === 0)
|
|
168813
|
+
throw httpError(400, "no editable field provided");
|
|
168814
|
+
const current = await getProposalStore().getById(id);
|
|
168815
|
+
if (!current)
|
|
168816
|
+
throw httpError(404, "not-found");
|
|
168817
|
+
if (projectId && current.projectId !== projectId)
|
|
168818
|
+
throw httpError(404, "project-mismatch");
|
|
168819
|
+
try {
|
|
168820
|
+
const updated = await getProposalStore().update(id, {
|
|
168821
|
+
...patch.rationale !== undefined ? { rationale: patch.rationale } : {},
|
|
168822
|
+
...patch.payload !== undefined ? { payload: patch.payload } : {}
|
|
168823
|
+
});
|
|
168824
|
+
if (!updated)
|
|
168825
|
+
throw httpError(404, "not-found");
|
|
168826
|
+
return { success: true, data: updated };
|
|
168827
|
+
} catch (e) {
|
|
168828
|
+
if (e instanceof ApiHttpError)
|
|
168829
|
+
throw e;
|
|
168830
|
+
const validationStatus = proposalPayloadValidationStatus(e);
|
|
168831
|
+
if (validationStatus !== null)
|
|
168832
|
+
throw httpError(validationStatus, e.message);
|
|
168833
|
+
throw e;
|
|
168834
|
+
}
|
|
168835
|
+
}
|
|
168836
|
+
async handleProposalDelete(id, body) {
|
|
168837
|
+
const projectId = body?.projectId;
|
|
168838
|
+
const current = await getProposalStore().getById(id);
|
|
168839
|
+
if (!current)
|
|
168840
|
+
throw httpError(404, "not-found");
|
|
168841
|
+
if (projectId && current.projectId !== projectId)
|
|
168842
|
+
throw httpError(404, "project-mismatch");
|
|
168843
|
+
const deletedId = await getProposalStore().delete(id);
|
|
168844
|
+
if (!deletedId)
|
|
168845
|
+
throw httpError(404, "not-found");
|
|
168846
|
+
return {
|
|
168847
|
+
success: true,
|
|
168848
|
+
data: {
|
|
168849
|
+
id: deletedId,
|
|
168850
|
+
...current.status === "approved" ? { note: "approved proposal's applied memory edit was not reversed" } : {}
|
|
168851
|
+
}
|
|
168852
|
+
};
|
|
168853
|
+
}
|
|
168277
168854
|
async handleSynapseSession(body) {
|
|
168278
168855
|
const registry3 = getSessionRegistry();
|
|
168279
168856
|
const sessionId = body.sessionId ?? newSynapseSessionId();
|
|
@@ -169873,6 +170450,39 @@ var HOOKS_EXEC_TOOL_DEFINITIONS = [
|
|
|
169873
170450
|
required: ["projectId"]
|
|
169874
170451
|
}
|
|
169875
170452
|
},
|
|
170453
|
+
{
|
|
170454
|
+
name: "handoff_update",
|
|
170455
|
+
description: "Edit a handoff's targetAgent/summary/openQuestions/nextSteps/files by id. Only these five fields are accepted \u2014 status, acceptedAt, id, projectId, createdAt and sourceSessionId are rejected by name; status/acceptedAt stay exclusively handoff_accept/handoff_cancel's. Refreshes the dual-written memory's content when a content-feeding field changes. Missing id or project mismatch return {ok:false, reason}. Never throws.",
|
|
170456
|
+
apiEndpoint: "/api/v1/handoff/:id",
|
|
170457
|
+
apiMethod: "PATCH",
|
|
170458
|
+
inputSchema: {
|
|
170459
|
+
type: "object",
|
|
170460
|
+
properties: {
|
|
170461
|
+
id: { type: "string", description: "Handoff id (required)" },
|
|
170462
|
+
projectId: { type: "string", description: "Optional project scope check (mismatch returns not-found)" },
|
|
170463
|
+
targetAgent: { type: "string", description: "Target agent name (or null to clear)" },
|
|
170464
|
+
summary: { type: "string", description: "Handoff summary" },
|
|
170465
|
+
openQuestions: { type: "array", items: { type: "string" } },
|
|
170466
|
+
nextSteps: { type: "array", items: { type: "string" } },
|
|
170467
|
+
files: { type: "array", items: { type: "string" } }
|
|
170468
|
+
},
|
|
170469
|
+
required: ["id"]
|
|
170470
|
+
}
|
|
170471
|
+
},
|
|
170472
|
+
{
|
|
170473
|
+
name: "handoff_delete",
|
|
170474
|
+
description: "Hard-delete a handoff by id, permitted in any status including open. The dual-written memory row is left intact (dangling metadata.handoffId is accepted, documented behaviour). Missing id or project mismatch return {ok:false, reason}. Never throws.",
|
|
170475
|
+
apiEndpoint: "/api/v1/handoff/:id",
|
|
170476
|
+
apiMethod: "DELETE",
|
|
170477
|
+
inputSchema: {
|
|
170478
|
+
type: "object",
|
|
170479
|
+
properties: {
|
|
170480
|
+
id: { type: "string", description: "Handoff id (required)" },
|
|
170481
|
+
projectId: { type: "string", description: "Optional project scope check (mismatch returns not-found)" }
|
|
170482
|
+
},
|
|
170483
|
+
required: ["id"]
|
|
170484
|
+
}
|
|
170485
|
+
},
|
|
169876
170486
|
{
|
|
169877
170487
|
name: "list_proposals",
|
|
169878
170488
|
description: "List pending auto-improvement proposals for a project (newest-first). The review-gate surfacing primitive: proposals are generated by the auto-improve loop from recurring patterns (repeated queries, hot files, common fixes). Never throws.",
|
|
@@ -169920,6 +170530,60 @@ var HOOKS_EXEC_TOOL_DEFINITIONS = [
|
|
|
169920
170530
|
required: ["id"]
|
|
169921
170531
|
}
|
|
169922
170532
|
},
|
|
170533
|
+
{
|
|
170534
|
+
name: "create_proposal",
|
|
170535
|
+
description: "Create a pending auto-improvement proposal by hand. kind must be memory.create/memory.update/memory.tag; payload is validated against the same per-kind rules the store enforces on read, so a write the reader would reject is refused here instead. Never throws.",
|
|
170536
|
+
apiEndpoint: "/api/v1/proposal/create",
|
|
170537
|
+
apiMethod: "POST",
|
|
170538
|
+
inputSchema: {
|
|
170539
|
+
type: "object",
|
|
170540
|
+
properties: {
|
|
170541
|
+
projectId: { type: "string", description: "Project identifier (required)" },
|
|
170542
|
+
kind: {
|
|
170543
|
+
type: "string",
|
|
170544
|
+
enum: ["memory.create", "memory.update", "memory.tag"],
|
|
170545
|
+
description: "Proposal kind (required)"
|
|
170546
|
+
},
|
|
170547
|
+
payload: {
|
|
170548
|
+
type: "object",
|
|
170549
|
+
description: "Kind-specific payload (required), validated against the same per-kind rules the store enforces on read"
|
|
170550
|
+
},
|
|
170551
|
+
rationale: { type: "string", description: "Optional rationale" },
|
|
170552
|
+
targetMemoryId: { type: "string", description: "Optional target memory id" }
|
|
170553
|
+
},
|
|
170554
|
+
required: ["projectId", "kind", "payload"]
|
|
170555
|
+
}
|
|
170556
|
+
},
|
|
170557
|
+
{
|
|
170558
|
+
name: "update_proposal",
|
|
170559
|
+
description: "Edit a proposal's rationale/payload by id, in any status. kind, targetMemoryId, status, decidedAt, id, projectId and createdAt are rejected by name \u2014 kind/targetMemoryId are immutable because applyProposal branches its whole behaviour on them. A payload edit re-validates against the row's existing kind. Missing id or project mismatch return {ok:false, reason}. Never throws.",
|
|
170560
|
+
apiEndpoint: "/api/v1/proposal/:id",
|
|
170561
|
+
apiMethod: "PATCH",
|
|
170562
|
+
inputSchema: {
|
|
170563
|
+
type: "object",
|
|
170564
|
+
properties: {
|
|
170565
|
+
id: { type: "string", description: "Proposal id (required)" },
|
|
170566
|
+
projectId: { type: "string", description: "Optional project scope check (mismatch returns not-found)" },
|
|
170567
|
+
rationale: { type: "string", description: "Replacement rationale" },
|
|
170568
|
+
payload: { type: "object", description: "Replacement payload, re-validated against the row's existing kind" }
|
|
170569
|
+
},
|
|
170570
|
+
required: ["id"]
|
|
170571
|
+
}
|
|
170572
|
+
},
|
|
170573
|
+
{
|
|
170574
|
+
name: "delete_proposal",
|
|
170575
|
+
description: "Hard-delete a proposal by id, permitted in any status. Deleting an already-approved proposal does not reverse the memory edit it already applied \u2014 the response says so explicitly via a note field. Missing id or project mismatch return {ok:false, reason}. Never throws.",
|
|
170576
|
+
apiEndpoint: "/api/v1/proposal/:id",
|
|
170577
|
+
apiMethod: "DELETE",
|
|
170578
|
+
inputSchema: {
|
|
170579
|
+
type: "object",
|
|
170580
|
+
properties: {
|
|
170581
|
+
id: { type: "string", description: "Proposal id (required)" },
|
|
170582
|
+
projectId: { type: "string", description: "Optional project scope check (mismatch returns not-found)" }
|
|
170583
|
+
},
|
|
170584
|
+
required: ["id"]
|
|
170585
|
+
}
|
|
170586
|
+
},
|
|
169923
170587
|
{
|
|
169924
170588
|
name: "execute",
|
|
169925
170589
|
description: "Run code in a detected polyglot sandbox runtime (js/ts/python/shell/ruby/go/rust/php/perl/r). " + "Returns stdout/stderr. Local-dev trust model: code runs on the host as the current user \u2014 " + "no OS-level isolation. Timeout default 30s, cap 300s. Pass `intent` to trim large outputs.",
|
|
@@ -170045,7 +170709,7 @@ var HOOKS_EXEC_TOOL_DEFINITIONS = [
|
|
|
170045
170709
|
|
|
170046
170710
|
// src/tool-definitions.ts
|
|
170047
170711
|
var BY_NAME = new Map([...PROJECT_TOOL_DEFINITIONS, ...SEARCH_TOOL_DEFINITIONS, ...MEMORY_TOOL_DEFINITIONS, ...SYNAPSE_TOOL_DEFINITIONS, ...HOOKS_EXEC_TOOL_DEFINITIONS].map((t) => [t.name, t]));
|
|
170048
|
-
var CANONICAL_ORDER = ["index", "index_status", "search", "remember", "recall", "memory_update", "memory_delete", "list_checkpoints", "create_checkpoint", "restore_checkpoint", "compress", "optimized_context", "analytics", "list_projects", "project_map", "get_architecture", "search_definitions", "get_references", "go_to_definition", "trace_path", "impact_analysis", "reset_project", "read_file", "synapse_session", "synapse_get", "synapse_update", "synapse_end", "synapse_prime", "synapse_access", "synapse_prefetch", "synapse_list", "synapse_task_begin", "synapse_task_end", "symbol_snippet", "memory_list", "reindex", "hook_ingest", "compact_snapshot", "bootstrap", "handoff_begin", "handoff_accept", "handoff_cancel", "handoff_list_pending", "list_proposals", "approve_proposal", "reject_proposal", "execute", "execute_file", "batch_execute", "fetch_and_index", "rename_project", "merge_projects", "profile_list", "profile_set"];
|
|
170712
|
+
var CANONICAL_ORDER = ["index", "index_status", "search", "remember", "recall", "memory_update", "memory_delete", "list_checkpoints", "create_checkpoint", "restore_checkpoint", "compress", "optimized_context", "analytics", "list_projects", "project_map", "get_architecture", "search_definitions", "get_references", "go_to_definition", "trace_path", "impact_analysis", "reset_project", "read_file", "synapse_session", "synapse_get", "synapse_update", "synapse_end", "synapse_prime", "synapse_access", "synapse_prefetch", "synapse_list", "synapse_task_begin", "synapse_task_end", "symbol_snippet", "memory_list", "reindex", "hook_ingest", "compact_snapshot", "bootstrap", "handoff_begin", "handoff_accept", "handoff_cancel", "handoff_list_pending", "handoff_update", "handoff_delete", "list_proposals", "approve_proposal", "reject_proposal", "create_proposal", "update_proposal", "delete_proposal", "execute", "execute_file", "batch_execute", "fetch_and_index", "rename_project", "merge_projects", "profile_list", "profile_set"];
|
|
170049
170713
|
var TOOL_DEFINITIONS = CANONICAL_ORDER.map((name26) => BY_NAME.get(name26));
|
|
170050
170714
|
function getToolDefinition(name26) {
|
|
170051
170715
|
return TOOL_DEFINITIONS.find((t) => t.name === name26);
|