Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 614fe44812 | |||
| 9ff933ba98 |
@@ -1,7 +1,12 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __esm = (fn, res) => function __init() {
|
||||
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
||||
var __esm = (fn, res, err2) => function __init() {
|
||||
if (err2) throw err2[0];
|
||||
try {
|
||||
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
||||
} catch (e) {
|
||||
throw err2 = [e], e;
|
||||
}
|
||||
};
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -102,7 +107,7 @@ function applyBaseRuntimeOptions(runtime, options) {
|
||||
function applyModuleEvalRuntimeOptions(runtime, options) {
|
||||
options.moduleLoader && runtime.setModuleLoader(options.moduleLoader), options.shouldInterrupt && runtime.setInterruptHandler(options.shouldInterrupt), options.memoryLimitBytes !== void 0 && runtime.setMemoryLimit(options.memoryLimitBytes), options.maxStackSizeBytes !== void 0 && runtime.setMaxStackSize(options.maxStackSizeBytes);
|
||||
}
|
||||
var __defProp2, __export2, QTS_DEBUG, errors_exports, QuickJSUnwrapError, QuickJSWrongOwner, QuickJSUseAfterFree, QuickJSNotImplemented, QuickJSAsyncifyError, QuickJSAsyncifySuspended, QuickJSMemoryLeakDetected, QuickJSEmscriptenModuleError, QuickJSUnknownIntrinsic, QuickJSPromisePending, QuickJSEmptyGetOwnPropertyNames, AwaitYield, UsingDisposable, SymbolDispose, prototypeAsAny, Lifetime, StaticLifetime, WeakLifetime, Scope, AbstractDisposableResult, DisposableSuccess, DisposableFail, DisposableResult, QuickJSDeferredPromise, ModuleMemory, UnstableSymbol, DefaultIntrinsics, QuickJSIterator, ContextMemory, QuickJSContext, QuickJSRuntime, QuickJSEmscriptenModuleCallbacks, QuickJSModuleCallbacks, QuickJSWASMModule;
|
||||
var __defProp2, __export2, QTS_DEBUG, errors_exports, QuickJSUnwrapError, QuickJSWrongOwner, QuickJSUseAfterFree, QuickJSNotImplemented, QuickJSAsyncifyError, QuickJSAsyncifySuspended, QuickJSMemoryLeakDetected, QuickJSEmscriptenModuleError, QuickJSUnknownIntrinsic, QuickJSPromisePending, QuickJSEmptyGetOwnPropertyNames, AwaitYield, UsingDisposable, SymbolDispose, prototypeAsAny, Lifetime, StaticLifetime, WeakLifetime, Scope, AbstractDisposableResult, DisposableSuccess, DisposableFail, DisposableResult, QuickJSDeferredPromise, ModuleMemory, DefaultIntrinsics, QuickJSIterator, ContextMemory, QuickJSContext, QuickJSRuntime, QuickJSEmscriptenModuleCallbacks, QuickJSModuleCallbacks, QuickJSWASMModule;
|
||||
var init_chunk_JTKJZQYV = __esm({
|
||||
"registry/components/code/node_modules/quickjs-emscripten-core/dist/chunk-JTKJZQYV.mjs"() {
|
||||
init_dist();
|
||||
@@ -190,7 +195,7 @@ var init_chunk_JTKJZQYV = __esm({
|
||||
return this.dispose();
|
||||
}
|
||||
};
|
||||
SymbolDispose = Symbol.dispose ?? Symbol.for("Symbol.dispose");
|
||||
SymbolDispose = Symbol.dispose ?? /* @__PURE__ */ Symbol.for("Symbol.dispose");
|
||||
prototypeAsAny = UsingDisposable.prototype;
|
||||
prototypeAsAny[SymbolDispose] || (prototypeAsAny[SymbolDispose] = function() {
|
||||
return this.dispose();
|
||||
@@ -409,7 +414,6 @@ Lifetime used`) : new QuickJSUseAfterFree("Lifetime not alive");
|
||||
return this.module._free(ptr), str;
|
||||
}
|
||||
};
|
||||
UnstableSymbol = Symbol("Unstable");
|
||||
DefaultIntrinsics = Object.freeze({ BaseObjects: true, Date: true, Eval: true, StringNormalize: true, RegExp: true, JSON: true, Proxy: true, MapSet: true, TypedArrays: true, Promise: true });
|
||||
QuickJSIterator = class extends UsingDisposable {
|
||||
constructor(handle, context) {
|
||||
@@ -773,7 +777,7 @@ ${cause.stack}Host: ${hostStack}`), Object.assign(exception, rest), exception;
|
||||
}
|
||||
return result.value;
|
||||
}
|
||||
[Symbol.for("nodejs.util.inspect.custom")]() {
|
||||
[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
|
||||
return this.alive ? `${this.constructor.name} { ctx: ${this.ctx.value} rt: ${this.rt.value} }` : `${this.constructor.name} { disposed }`;
|
||||
}
|
||||
getFunction(fn_id) {
|
||||
@@ -909,7 +913,7 @@ ${cause.stack}Host: ${hostStack}`), Object.assign(exception, rest), exception;
|
||||
debugLog(...msg) {
|
||||
this._debugMode && console.log("quickjs-emscripten:", ...msg);
|
||||
}
|
||||
[Symbol.for("nodejs.util.inspect.custom")]() {
|
||||
[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
|
||||
return this.alive ? `${this.constructor.name} { rt: ${this.rt.value} }` : `${this.constructor.name} { disposed }`;
|
||||
}
|
||||
getSystemContext() {
|
||||
@@ -1339,10 +1343,10 @@ async function QuickJSRaw(moduleArg = {}) {
|
||||
x ? (0 === h && (h = ra()), g[m] = x(e[m])) : g[m] = e[m];
|
||||
}
|
||||
b = a(...g);
|
||||
return b = function(k) {
|
||||
return b = (function(k) {
|
||||
0 !== h && sa(h);
|
||||
return "string" === d ? R(k) : "boolean" === d ? !!k : k;
|
||||
}(b);
|
||||
})(b);
|
||||
};
|
||||
c.wasmMemory ? r = c.wasmMemory : r = new WebAssembly.Memory({ initial: (c.INITIAL_MEMORY || 16777216) / 65536, maximum: 32768 });
|
||||
K();
|
||||
@@ -1464,7 +1468,7 @@ async function QuickJSRaw(moduleArg = {}) {
|
||||
}, t: function(a, d) {
|
||||
c.callbacks.freeHostRef(void 0, a, d);
|
||||
} }, Z;
|
||||
Z = await async function() {
|
||||
Z = await (async function() {
|
||||
function a(b) {
|
||||
b = Z = b.exports;
|
||||
c._malloc = b.v;
|
||||
@@ -1551,7 +1555,7 @@ async function QuickJSRaw(moduleArg = {}) {
|
||||
});
|
||||
M ??= c.locateFile ? c.locateFile ? c.locateFile("emscripten-module.wasm", u) : u + "emscripten-module.wasm" : new URL("emscripten-module.wasm", import.meta.url).href;
|
||||
return a((await ea(d)).instance);
|
||||
}();
|
||||
})();
|
||||
(function() {
|
||||
function a() {
|
||||
c.calledRun = true;
|
||||
@@ -3034,7 +3038,7 @@ var Hono = class _Hono {
|
||||
var emptyParam = [];
|
||||
function match(method, path) {
|
||||
const matchers = this.buildAllMatchers();
|
||||
const match2 = (method2, path2) => {
|
||||
const match2 = ((method2, path2) => {
|
||||
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
|
||||
const staticMatch = matcher[2][path2];
|
||||
if (staticMatch) {
|
||||
@@ -3046,7 +3050,7 @@ function match(method, path) {
|
||||
}
|
||||
const index = match3.indexOf("", 1);
|
||||
return [matcher[1][index], match3];
|
||||
};
|
||||
});
|
||||
this.match = match2;
|
||||
return match2(method, path);
|
||||
}
|
||||
@@ -4008,7 +4012,7 @@ app.post("/", async (c) => {
|
||||
);
|
||||
}
|
||||
});
|
||||
var code_default = app;
|
||||
var index_default = app;
|
||||
export {
|
||||
code_default as default
|
||||
index_default as default
|
||||
};
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __esm = (fn, res) => function __init() {
|
||||
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
||||
var __esm = (fn, res, err) => function __init() {
|
||||
if (err) throw err[0];
|
||||
try {
|
||||
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
||||
} catch (e) {
|
||||
throw err = [e], e;
|
||||
}
|
||||
};
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -1501,7 +1506,7 @@ var init_hono_base = __esm({
|
||||
// cypher-executor/node_modules/.pnpm/hono@4.12.10/node_modules/hono/dist/router/reg-exp-router/matcher.js
|
||||
function match(method, path) {
|
||||
const matchers = this.buildAllMatchers();
|
||||
const match2 = (method2, path2) => {
|
||||
const match2 = ((method2, path2) => {
|
||||
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
|
||||
const staticMatch = matcher[2][path2];
|
||||
if (staticMatch) {
|
||||
@@ -1513,7 +1518,7 @@ function match(method, path) {
|
||||
}
|
||||
const index = match3.indexOf("", 1);
|
||||
return [matcher[1][index], match3];
|
||||
};
|
||||
});
|
||||
this.match = match2;
|
||||
return match2(method, path);
|
||||
}
|
||||
@@ -7433,7 +7438,7 @@ var init_lib = __esm({
|
||||
...processCreateParams(params)
|
||||
});
|
||||
};
|
||||
BRAND = Symbol("zod_brand");
|
||||
BRAND = /* @__PURE__ */ Symbol("zod_brand");
|
||||
ZodBranded = class extends ZodType {
|
||||
_parse(input) {
|
||||
const { ctx } = this._processInputParams(input);
|
||||
@@ -7607,14 +7612,14 @@ var init_lib = __esm({
|
||||
onumber = () => numberType().optional();
|
||||
oboolean = () => booleanType().optional();
|
||||
coerce = {
|
||||
string: (arg) => ZodString.create({ ...arg, coerce: true }),
|
||||
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
|
||||
boolean: (arg) => ZodBoolean.create({
|
||||
string: ((arg) => ZodString.create({ ...arg, coerce: true })),
|
||||
number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),
|
||||
boolean: ((arg) => ZodBoolean.create({
|
||||
...arg,
|
||||
coerce: true
|
||||
}),
|
||||
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
|
||||
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
||||
})),
|
||||
bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),
|
||||
date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
|
||||
};
|
||||
NEVER = INVALID;
|
||||
z = /* @__PURE__ */ Object.freeze({
|
||||
@@ -7835,6 +7840,7 @@ var init_recipe_loader = __esm({
|
||||
super(message);
|
||||
this.recipe = recipe;
|
||||
}
|
||||
recipe;
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -8841,7 +8847,7 @@ function recordRecipeStats(env, recipeKeys, ok, at, ctx) {
|
||||
)
|
||||
).then(() => void 0);
|
||||
if (ctx?.waitUntil) ctx.waitUntil(promise);
|
||||
else ;
|
||||
else void promise;
|
||||
}
|
||||
function generateToken() {
|
||||
const tokenBytes = crypto.getRandomValues(new Uint8Array(16));
|
||||
@@ -8883,7 +8889,7 @@ async function executeWebhookGraph(env, graph, triggerContext, token, apiKey, ct
|
||||
result.trace
|
||||
);
|
||||
if (ctx?.waitUntil) ctx.waitUntil(statsPromise);
|
||||
else ;
|
||||
else void statsPromise;
|
||||
}
|
||||
return { success: true, data: result.data, duration_ms };
|
||||
} catch (err) {
|
||||
@@ -8907,7 +8913,7 @@ async function executeWebhookGraph(env, graph, triggerContext, token, apiKey, ct
|
||||
err.trace
|
||||
);
|
||||
if (ctx?.waitUntil) ctx.waitUntil(statsPromise);
|
||||
else ;
|
||||
else void statsPromise;
|
||||
}
|
||||
if (err instanceof ExecutionError) {
|
||||
const traceFormatted = err.trace.map((s) => ({
|
||||
@@ -9374,6 +9380,7 @@ function extractTarget(input) {
|
||||
return typeof raw2 === "string" ? raw2 : JSON.stringify(raw2);
|
||||
}
|
||||
async function writeExecutionVerdict(env, workflowId, nodes, verdict, durationMs, message, input, apiKey) {
|
||||
void nodes;
|
||||
try {
|
||||
const { base, headers } = kbdbBase(env);
|
||||
await fetch(`${base}/execution-log/record`, {
|
||||
@@ -15654,10 +15661,10 @@ app.route("/", consoleAuthRouter);
|
||||
app.route("/", consoleDashboardRouter);
|
||||
app.route("/", portalRouter);
|
||||
app.route("/", portalDataRouter);
|
||||
var src_default = {
|
||||
var index_default = {
|
||||
fetch: app.fetch,
|
||||
scheduled: handleScheduled
|
||||
};
|
||||
export {
|
||||
src_default as default
|
||||
index_default as default
|
||||
};
|
||||
|
||||
@@ -1427,7 +1427,7 @@ var Hono = class _Hono {
|
||||
var emptyParam = [];
|
||||
function match(method, path) {
|
||||
const matchers = this.buildAllMatchers();
|
||||
const match2 = (method2, path2) => {
|
||||
const match2 = ((method2, path2) => {
|
||||
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
|
||||
const staticMatch = matcher[2][path2];
|
||||
if (staticMatch) {
|
||||
@@ -1439,7 +1439,7 @@ function match(method, path) {
|
||||
}
|
||||
const index = match3.indexOf("", 1);
|
||||
return [matcher[1][index], match3];
|
||||
};
|
||||
});
|
||||
this.match = match2;
|
||||
return match2(method, path);
|
||||
}
|
||||
@@ -2522,7 +2522,7 @@ app.post("/", async (c) => {
|
||||
);
|
||||
}
|
||||
});
|
||||
var src_default = app;
|
||||
var index_default = app;
|
||||
async function runWasm(input) {
|
||||
const hostFunctions = {
|
||||
http_request: async (url, method, headersJson, body) => {
|
||||
@@ -2568,5 +2568,5 @@ async function runWasm(input) {
|
||||
return JSON.parse(stdout);
|
||||
}
|
||||
export {
|
||||
src_default as default
|
||||
index_default as default
|
||||
};
|
||||
|
||||
@@ -1444,7 +1444,7 @@ var Hono = class _Hono {
|
||||
var emptyParam = [];
|
||||
function match(method, path) {
|
||||
const matchers = this.buildAllMatchers();
|
||||
const match2 = (method2, path2) => {
|
||||
const match2 = ((method2, path2) => {
|
||||
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
|
||||
const staticMatch = matcher[2][path2];
|
||||
if (staticMatch) {
|
||||
@@ -1456,7 +1456,7 @@ function match(method, path) {
|
||||
}
|
||||
const index = match3.indexOf("", 1);
|
||||
return [matcher[1][index], match3];
|
||||
};
|
||||
});
|
||||
this.match = match2;
|
||||
return match2(method, path);
|
||||
}
|
||||
@@ -4157,7 +4157,7 @@ app.route("/recipe-stats", recipeStatRoutes);
|
||||
app.route("/execution-log", executionLogRoutes);
|
||||
app.route("/embed", embedRoutes);
|
||||
app.route("/map", mapRoutes);
|
||||
var src_default = app;
|
||||
var index_default = app;
|
||||
export {
|
||||
src_default as default
|
||||
index_default as default
|
||||
};
|
||||
|
||||
@@ -5,7 +5,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __commonJS = (cb, mod) => function __require() {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
try {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
} catch (e) {
|
||||
throw mod = 0, e;
|
||||
}
|
||||
};
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -8223,7 +8227,7 @@ var Hono = class _Hono {
|
||||
var emptyParam = [];
|
||||
function match(method, path) {
|
||||
const matchers = this.buildAllMatchers();
|
||||
const match2 = (method2, path2) => {
|
||||
const match2 = ((method2, path2) => {
|
||||
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
|
||||
const staticMatch = matcher[2][path2];
|
||||
if (staticMatch) {
|
||||
@@ -8235,7 +8239,7 @@ function match(method, path) {
|
||||
}
|
||||
const index = match3.indexOf("", 1);
|
||||
return [matcher[1][index], match3];
|
||||
};
|
||||
});
|
||||
this.match = match2;
|
||||
return match2(method, path);
|
||||
}
|
||||
@@ -12959,7 +12963,7 @@ ZodNaN.create = (params) => {
|
||||
...processCreateParams(params)
|
||||
});
|
||||
};
|
||||
var BRAND = Symbol("zod_brand");
|
||||
var BRAND = /* @__PURE__ */ Symbol("zod_brand");
|
||||
var ZodBranded = class extends ZodType {
|
||||
_parse(input) {
|
||||
const { ctx } = this._processInputParams(input);
|
||||
@@ -13161,14 +13165,14 @@ var ostring = () => stringType().optional();
|
||||
var onumber = () => numberType().optional();
|
||||
var oboolean = () => booleanType().optional();
|
||||
var coerce = {
|
||||
string: (arg) => ZodString.create({ ...arg, coerce: true }),
|
||||
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
|
||||
boolean: (arg) => ZodBoolean.create({
|
||||
string: ((arg) => ZodString.create({ ...arg, coerce: true })),
|
||||
number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),
|
||||
boolean: ((arg) => ZodBoolean.create({
|
||||
...arg,
|
||||
coerce: true
|
||||
}),
|
||||
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
|
||||
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
||||
})),
|
||||
bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),
|
||||
date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
|
||||
};
|
||||
var NEVER = INVALID;
|
||||
|
||||
@@ -13219,7 +13223,6 @@ function $constructor(name, initializer3, params) {
|
||||
Object.defineProperty(_, "name", { value: name });
|
||||
return _;
|
||||
}
|
||||
var $brand = Symbol("zod_brand");
|
||||
var $ZodAsyncError = class extends Error {
|
||||
constructor() {
|
||||
super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);
|
||||
@@ -15723,8 +15726,6 @@ function en_default2() {
|
||||
}
|
||||
|
||||
// mcp/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/registries.js
|
||||
var $output = Symbol("ZodOutput");
|
||||
var $input = Symbol("ZodInput");
|
||||
var $ZodRegistry = class {
|
||||
constructor() {
|
||||
this._map = /* @__PURE__ */ new Map();
|
||||
@@ -17002,10 +17003,10 @@ var ZodMiniType = /* @__PURE__ */ $constructor("ZodMiniType", (inst, def) => {
|
||||
};
|
||||
inst.clone = (_def, params) => clone(inst, _def, params);
|
||||
inst.brand = () => inst;
|
||||
inst.register = (reg, meta) => {
|
||||
inst.register = ((reg, meta) => {
|
||||
reg.add(inst, meta);
|
||||
return inst;
|
||||
};
|
||||
});
|
||||
});
|
||||
var ZodMiniObject = /* @__PURE__ */ $constructor("ZodMiniObject", (inst, def) => {
|
||||
$ZodObject.init(inst, def);
|
||||
@@ -17268,10 +17269,10 @@ var ZodType2 = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
|
||||
};
|
||||
inst.clone = (def2, params) => clone(inst, def2, params);
|
||||
inst.brand = () => inst;
|
||||
inst.register = (reg, meta) => {
|
||||
inst.register = ((reg, meta) => {
|
||||
reg.add(inst, meta);
|
||||
return inst;
|
||||
};
|
||||
});
|
||||
inst.parse = (data, params) => parse2(inst, data, params, { callee: inst.parse });
|
||||
inst.safeParse = (data, params) => safeParse3(inst, data, params);
|
||||
inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
|
||||
@@ -19243,11 +19244,13 @@ function assertCompleteRequestPrompt(request) {
|
||||
if (request.params.ref.type !== "ref/prompt") {
|
||||
throw new TypeError(`Expected CompleteRequestPrompt, but got ${request.params.ref.type}`);
|
||||
}
|
||||
void request;
|
||||
}
|
||||
function assertCompleteRequestResourceTemplate(request) {
|
||||
if (request.params.ref.type !== "ref/resource") {
|
||||
throw new TypeError(`Expected CompleteRequestResourceTemplate, but got ${request.params.ref.type}`);
|
||||
}
|
||||
void request;
|
||||
}
|
||||
var CompleteResultSchema = ResultSchema.extend({
|
||||
completion: looseObject({
|
||||
@@ -19400,7 +19403,7 @@ function isTerminal(status) {
|
||||
}
|
||||
|
||||
// mcp/node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Options.js
|
||||
var ignoreOverride = Symbol("Let zodToJsonSchema decide on which parser to use");
|
||||
var ignoreOverride = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use");
|
||||
var defaultOptions = {
|
||||
name: void 0,
|
||||
$refStrategy: "root",
|
||||
@@ -22376,7 +22379,7 @@ var Server = class extends Protocol {
|
||||
};
|
||||
|
||||
// mcp/node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
|
||||
var COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
|
||||
var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
|
||||
function isCompletable(schema4) {
|
||||
return !!schema4 && typeof schema4 === "object" && COMPLETABLE_SYMBOL in schema4;
|
||||
}
|
||||
@@ -24691,13 +24694,13 @@ function registerAllIntrospectionTools(server, env) {
|
||||
}
|
||||
|
||||
// mcp/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/nodes/identity.js
|
||||
var ALIAS = Symbol.for("yaml.alias");
|
||||
var DOC = Symbol.for("yaml.document");
|
||||
var MAP = Symbol.for("yaml.map");
|
||||
var PAIR = Symbol.for("yaml.pair");
|
||||
var SCALAR = Symbol.for("yaml.scalar");
|
||||
var SEQ = Symbol.for("yaml.seq");
|
||||
var NODE_TYPE = Symbol.for("yaml.node.type");
|
||||
var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
|
||||
var DOC = /* @__PURE__ */ Symbol.for("yaml.document");
|
||||
var MAP = /* @__PURE__ */ Symbol.for("yaml.map");
|
||||
var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair");
|
||||
var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar");
|
||||
var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq");
|
||||
var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type");
|
||||
var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
|
||||
var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
|
||||
var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
|
||||
@@ -24727,9 +24730,9 @@ function isNode(node) {
|
||||
var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor;
|
||||
|
||||
// mcp/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/visit.js
|
||||
var BREAK = Symbol("break visit");
|
||||
var SKIP = Symbol("skip children");
|
||||
var REMOVE = Symbol("remove node");
|
||||
var BREAK = /* @__PURE__ */ Symbol("break visit");
|
||||
var SKIP = /* @__PURE__ */ Symbol("skip children");
|
||||
var REMOVE = /* @__PURE__ */ Symbol("remove node");
|
||||
function visit(node, visitor) {
|
||||
const visitor_ = initVisitor(visitor);
|
||||
if (isDocument(node)) {
|
||||
@@ -29322,9 +29325,9 @@ ${end.comment}` : end.comment;
|
||||
};
|
||||
|
||||
// mcp/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/parse/cst-visit.js
|
||||
var BREAK2 = Symbol("break visit");
|
||||
var SKIP2 = Symbol("skip children");
|
||||
var REMOVE2 = Symbol("remove item");
|
||||
var BREAK2 = /* @__PURE__ */ Symbol("break visit");
|
||||
var SKIP2 = /* @__PURE__ */ Symbol("skip children");
|
||||
var REMOVE2 = /* @__PURE__ */ Symbol("remove item");
|
||||
function visit2(cst, visitor) {
|
||||
if ("type" in cst && cst.type === "document")
|
||||
cst = { start: cst.start, value: cst.value };
|
||||
@@ -33596,7 +33599,7 @@ app.post("/", partnerAuthMiddleware, async (c) => {
|
||||
const identity = resolveKnowledgeIdentity(c.get("auth_path"), c.get("portal"));
|
||||
return handleMcpRequest(c.req.raw, c.env, orgNamespace, partnerToken, identity);
|
||||
});
|
||||
var src_default = _app;
|
||||
var index_default = _app;
|
||||
export {
|
||||
src_default as default
|
||||
index_default as default
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"built_for": "arcrun-tier2-worker-artifacts",
|
||||
"generated_at": "2026-08-12T16:16:33.433Z",
|
||||
"repo_head": "b223a698844be289c1b01f99eb34a8e2ac85bb74",
|
||||
"generated_at": "2026-08-12T16:25:19.210Z",
|
||||
"repo_head": "2fcae722e7d7eab3c8b39744b87ae1344d23ba2a",
|
||||
"repo_dirty": false,
|
||||
"workers": [
|
||||
{
|
||||
@@ -11,8 +11,8 @@
|
||||
"source_commit": "b223a698844be289c1b01f99eb34a8e2ac85bb74",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-cypher-executor/worker.mjs",
|
||||
"js_bytes": 588397,
|
||||
"content_sha256": "e0026a23792f8b6b02e35c91081604b9a761c608a2cae0e6ee8ab0f34a484501",
|
||||
"js_bytes": 588587,
|
||||
"content_sha256": "c43728d21251f7835497d7dc40a3e702526a70b33406b617ed64126ffb16e1e0",
|
||||
"modules": [],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [
|
||||
@@ -61,8 +61,8 @@
|
||||
"source_commit": "f87d0e92f49690253e7c89c5badc82a08eb5d21b",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-kbdb/worker.mjs",
|
||||
"js_bytes": 149791,
|
||||
"content_sha256": "9c6d41895d78cbf3048fb539690591c86071b7d6ebf7fb0b8b51607c6b1f8ee9",
|
||||
"js_bytes": 149797,
|
||||
"content_sha256": "8b23853cbc88aee0ca15ef20ca46e92bd8e75064cd311af2847f4d51811960b1",
|
||||
"modules": [],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [
|
||||
@@ -90,8 +90,8 @@
|
||||
"source_commit": "1e85dfb49b0e8d81c0854781d93ee4e6a300c7b3",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-http-request/worker.mjs",
|
||||
"js_bytes": 80073,
|
||||
"content_sha256": "9a9dcb71879a7bdfd9fec1bd94eb9742e12cb63733d822ce63eeb1be30008d15",
|
||||
"js_bytes": 80079,
|
||||
"content_sha256": "cdd97364f277587cbade69e09bb40812c68f26a1e8bc9aa632c65b1b962b0b85",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
@@ -122,8 +122,8 @@
|
||||
"source_commit": "621cb8d948d61be6202063fd02effb3f538437fe",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-code/worker.mjs",
|
||||
"js_bytes": 153671,
|
||||
"content_sha256": "285a7406ec694ae47dccfaf48517f712c74d207a1689dffa15c39f1555b45be5",
|
||||
"js_bytes": 153758,
|
||||
"content_sha256": "751634a3fc9a99cc2da662026818d754c48f031d10bff3b3be2d3a8ee2311bd6",
|
||||
"modules": [
|
||||
{
|
||||
"name": "quickjs.wasm",
|
||||
@@ -151,8 +151,8 @@
|
||||
"source_commit": "10d150ac2b4385af95a457f3c411430c4a146cf9",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-mcp/worker.mjs",
|
||||
"js_bytes": 1179229,
|
||||
"content_sha256": "3ebc0d441bc04ae56a1205507da701f93bed9efa9b1e53c1777c04cbef5bdb67",
|
||||
"js_bytes": 1179487,
|
||||
"content_sha256": "1cd4c4d079d72bf7cba7c490ba6a88476f70b3ea51af7e5c93f9a184ae3c0ce6",
|
||||
"modules": [],
|
||||
"compat_date": "2024-11-27",
|
||||
"compat_flags": [
|
||||
|
||||
@@ -840,7 +840,12 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
|
||||
'<div class="kt">' + esc(l.library) + (S.library === l.library ? ' <span class="tag" style="font-size:11.5px;vertical-align:middle">搜尋中</span>' : '') + '</div>' +
|
||||
'<div class="ks">' + (l.narrative ? esc(l.narrative) : '<span class="dim">(此庫尚無 narrative——recompute 時可帶入)</span>') + '</div>' +
|
||||
(ents && ents.length ? '<div style="display:flex;gap:6px;flex-wrap:wrap">' + lmEntityLine(ents) + '</div>' : '') +
|
||||
'<div class="km"><span class="mono" style="color:var(--amber)">' + (Number(l.triplet_count) || 0) + ' 三元組</span>' +
|
||||
// Arcrun#87 三次收尾(2026-08-13):triplet_count=0 不等於這庫沒有知識——entries 有標庫、
|
||||
// 只是三元組萃取沒對它跑過(kb 以外幾乎全庫皆此況)。entry_count>0 時附一句,別讓看板
|
||||
// 的「0 三元組」被讀成「這庫是空的」(同 MCP 端 kbdb_map.ts 的 entryOnlyHint 同一件事)。
|
||||
'<div class="km"><span class="mono" style="color:var(--amber)">' + (Number(l.triplet_count) || 0) + ' 三元組' +
|
||||
(!(Number(l.triplet_count) || 0) && (Number(l.entry_count) || 0) > 0
|
||||
? '<span class="dim">・' + (Number(l.entry_count) || 0) + ' 筆原始內容(尚未萃取)</span>' : '') + '</span>' +
|
||||
'<span class="dim" style="margin-left:auto;font-size:12.5px">' + stamp + '</span></div></div>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
@@ -38,6 +38,13 @@ export interface LibraryMapRow {
|
||||
narrative: string | null;
|
||||
top_entities: string[]; // 全館視圖只回 top 3 名字(數百 token 內,design §4 MCP instructions 用)
|
||||
triplet_count: number;
|
||||
// Arcrun#87 三次收尾(2026-08-13,「藏書地圖說實話」):triplet_count=0 不等於「這庫沒有知識」——
|
||||
// entries(ingest 進來的原始卡片/skill 內容)與 triplet(從 entries 萃取出的三元組)是兩件事,
|
||||
// 一個庫可能有大量 entries、但三元組萃取從沒對它跑過(實測:kb 以外 7 庫皆此況,entries 存在
|
||||
// 且 library 標記正確,triplet 一筆都沒有)。entry_count 讓讀端(AI/MCP 渲染層)分得清
|
||||
// 「真的沒東西」與「有東西但還沒被萃取成三元組」,不再把後者誤讀成前者、誤判庫是空的而放棄查詢
|
||||
// (見本檔 liveEntryCountsByLibrary 的計數口徑)。
|
||||
entry_count: number;
|
||||
updated_at: number;
|
||||
}
|
||||
|
||||
@@ -50,6 +57,7 @@ export interface LibraryMapDetail {
|
||||
relation_profile: RelationStat[];
|
||||
bridges: Bridge[];
|
||||
triplet_count: number;
|
||||
entry_count: number; // 同 LibraryMapRow.entry_count(見該欄位註解)
|
||||
commit_hash: string | null;
|
||||
status: string;
|
||||
updated_at: number;
|
||||
@@ -285,6 +293,10 @@ export async function recomputeLibraryMap(db: D1Database, input: RecomputeInput)
|
||||
superseded.push(row.rid);
|
||||
}
|
||||
|
||||
// entry_count(Arcrun#87 三次收尾):recompute 只重算三元組那一半,entry_count 是即時算的
|
||||
// 另一半,兩者同樣的道理一起回傳(見 LibraryMapDetail.entry_count 欄位註解)。
|
||||
const entryCount = (await liveEntryCountsByLibrary(db, owner)).get(library) ?? 0;
|
||||
|
||||
return {
|
||||
map: {
|
||||
record_id: blockEntry.id,
|
||||
@@ -295,6 +307,7 @@ export async function recomputeLibraryMap(db: D1Database, input: RecomputeInput)
|
||||
relation_profile: relationProfile,
|
||||
bridges,
|
||||
triplet_count: tripletCount,
|
||||
entry_count: entryCount,
|
||||
commit_hash: input.commit_hash ?? null,
|
||||
status: 'active',
|
||||
updated_at: blockEntry.created_at,
|
||||
@@ -367,6 +380,34 @@ async function liveTripletCountsByLibrary(
|
||||
return m;
|
||||
}
|
||||
|
||||
// 這個 owner 底下、依 entries 自己的 metadata_json.$.library 分組的即時「原始內容」數
|
||||
//(Arcrun#87 三次收尾,2026-08-13)——與 liveTripletCountsByLibrary 算的是兩張完全不同的帳:
|
||||
// 那個算「萃取出幾條三元組」,這個算「ingest 進來幾筆原始卡片/skill/block」。兩者天生會不一樣
|
||||
// (萃取是下游、有延遲甚至從沒對某些庫跑過),刻意分開算、分開回傳,不是同一數字的兩種寫法。
|
||||
//
|
||||
// 排除口徑(都是結構性排除,不是內容語意判斷——沒有違反「base 對內容語意無知」):
|
||||
// - entry_type='value':record-crud.ts 的通用機制,任何 template(含 triplet 本身)建 record
|
||||
// 時每個 slot 值都會落一筆這種 entry,是儲存實作細節,不是使用者看得到的「一筆知識」。
|
||||
// - metadata.kind='library_map' 的 block:地圖自己每次 recompute 產出的摘要 block(含歷史
|
||||
// superseded 的),算進去會自我膨脹、且無限接近雞生蛋——地圖不該把自己算進地圖裡。
|
||||
async function liveEntryCountsByLibrary(db: D1Database, owner_id?: string): Promise<LibraryCountMap> {
|
||||
const params: unknown[] = owner_id ? [owner_id] : [];
|
||||
const res = await db
|
||||
.prepare( // kbdb-sql-ok:牆內本體(kbdb/src/actions/),checkout 開在巢狀 worktree /private/tmp/wt-arcrun-library-map-honesty-87/(同 962d863/5919c6b 已記載的假警報成因:hook 逐字比對 matrix/arcrun/kbdb/src/ 吃不到中間多出的 worktree 目錄層,非繞牆)
|
||||
`SELECT COALESCE(NULLIF(json_extract(metadata_json, '$.library'), ''), 'general') AS library,
|
||||
COUNT(*) AS n
|
||||
FROM entries
|
||||
WHERE ${owner_id ? 'owner_id = ? AND ' : ''}entry_type != 'value'
|
||||
AND NOT (entry_type = 'block' AND COALESCE(json_extract(metadata_json, '$.kind'), '') = 'library_map')
|
||||
GROUP BY COALESCE(NULLIF(json_extract(metadata_json, '$.library'), ''), 'general')`,
|
||||
)
|
||||
.bind(...params)
|
||||
.all<{ library: string; n: number }>();
|
||||
const m: LibraryCountMap = new Map();
|
||||
for (const r of res.results ?? []) m.set(r.library, r.n);
|
||||
return m;
|
||||
}
|
||||
|
||||
// 「已知庫名」集合:即使目前三元組數是 0,只要蓋過章(entries metadata.library,t52 慣例)或
|
||||
// 登記過(portal_library record),就不算「查無此庫」——用來分辨 GET /map/:library 的
|
||||
// 「這庫是空的」(回 200+triplet_count:0)vs「查無此庫」(回 404)。kbdb base 對 portal_library
|
||||
@@ -453,20 +494,26 @@ interface MapPivotRow {
|
||||
ts: number;
|
||||
}
|
||||
|
||||
// 全館地圖:每庫一行(library+narrative+top 3 entities+triplet_count),MCP instructions
|
||||
// 直接嵌用(設計上限=數百 token,R3)。template 還不存在(從未 recompute)→ 誠實回空清單。
|
||||
// 全館地圖:每庫一行(library+narrative+top 3 entities+triplet_count+entry_count),MCP
|
||||
// instructions 直接嵌用(設計上限=數百 token,R3)。template 還不存在(從未 recompute)→
|
||||
// 誠實回空清單。entry_count 與 triplet_count 並排回傳(Arcrun#87 三次收尾):一個查「原始內容
|
||||
// 有沒有」、一個查「萃取出幾條三元組」,兩者不同源、允許不一致(見 liveEntryCountsByLibrary
|
||||
// 註解),讀端不該把其中一個的 0 當成另一個的答案。
|
||||
export async function listLibraryMaps(db: D1Database, owner_id?: string): Promise<LibraryMapRow[]> {
|
||||
const tpl = await getTemplate(db, LIBRARY_MAP_TEMPLATE_NAME);
|
||||
if (!tpl) return [];
|
||||
const params: unknown[] = owner_id ? [tpl.id, owner_id] : [tpl.id];
|
||||
const res = await db
|
||||
.prepare(
|
||||
`WITH m AS (${mapPivotSql(!!owner_id)})
|
||||
SELECT * FROM m WHERE COALESCE(m.status, 'active') = 'active' AND m.library IS NOT NULL
|
||||
ORDER BY m.ts DESC`,
|
||||
)
|
||||
.bind(...params)
|
||||
.all<MapPivotRow>();
|
||||
const [res, entryCounts] = await Promise.all([
|
||||
db
|
||||
.prepare( // kbdb-sql-ok:牆內本體(kbdb/src/actions/),既有查詢(listLibraryMaps 原本就有)此次改包進 Promise.all 才重新觸發掃描,非新增違規;worktree 路徑假警報同上方 liveEntryCountsByLibrary 註解
|
||||
`WITH m AS (${mapPivotSql(!!owner_id)})
|
||||
SELECT * FROM m WHERE COALESCE(m.status, 'active') = 'active' AND m.library IS NOT NULL
|
||||
ORDER BY m.ts DESC`,
|
||||
)
|
||||
.bind(...params)
|
||||
.all<MapPivotRow>(),
|
||||
liveEntryCountsByLibrary(db, owner_id),
|
||||
]);
|
||||
// 每庫只留最新 active(supersede 失敗殘留多個 active 時,讀端自癒取最新——順序安全的另一半)。
|
||||
const byLib = new Map<string, LibraryMapRow>();
|
||||
for (const r of res.results ?? []) {
|
||||
@@ -476,6 +523,7 @@ export async function listLibraryMaps(db: D1Database, owner_id?: string): Promis
|
||||
narrative: r.narrative || null,
|
||||
top_entities: parseJsonArray<TopEntity>(r.top_entities).slice(0, 3).map((t) => t.name),
|
||||
triplet_count: Number(r.triplet_count ?? 0) || 0,
|
||||
entry_count: entryCounts.get(r.library) ?? 0,
|
||||
updated_at: r.ts,
|
||||
});
|
||||
}
|
||||
@@ -501,7 +549,12 @@ export async function getLibraryMapDetail(
|
||||
.first<MapPivotRow>();
|
||||
if (!row) return null;
|
||||
// record_id=map block entry id(recompute 寫入時綁定);entry 若被外力刪除,content 誠實回 null。
|
||||
const blockEntry = await getEntry(db, row.rid);
|
||||
// entry_count 與全館視圖(listLibraryMaps)同一套計法(liveEntryCountsByLibrary),單庫詳圖
|
||||
// 只取自己那一庫的數字——Arcrun#87 三次收尾,理由見 LibraryMapDetail.entry_count 欄位註解。
|
||||
const [blockEntry, entryCounts] = await Promise.all([
|
||||
getEntry(db, row.rid),
|
||||
liveEntryCountsByLibrary(db, owner_id),
|
||||
]);
|
||||
return {
|
||||
record_id: row.rid,
|
||||
library,
|
||||
@@ -511,6 +564,7 @@ export async function getLibraryMapDetail(
|
||||
relation_profile: parseJsonArray<RelationStat>(row.relation_profile),
|
||||
bridges: parseJsonArray<Bridge>(row.bridges),
|
||||
triplet_count: Number(row.triplet_count ?? 0) || 0,
|
||||
entry_count: entryCounts.get(library) ?? 0,
|
||||
commit_hash: row.commit_hash || null,
|
||||
status: row.status ?? 'active',
|
||||
updated_at: row.ts,
|
||||
|
||||
@@ -361,6 +361,63 @@ describe('M3 收尾 — 即時新鮮度(ensureFreshLibraryMaps,讀端自動
|
||||
expect(unknown.status).toBe(404); // 真的從沒出現過的名字才 404
|
||||
});
|
||||
|
||||
it('entry_count 讓「有原始內容但三元組從沒萃取過」與「真的什麼都沒有」分得清(Arcrun#87 三次收尾)', async () => {
|
||||
// 情境沿用上一案的 'hr' 庫(entries 蓋過章、triplet_count:0),這正是 leo21c 實測到的真實
|
||||
// 現況(kb 以外 7 庫皆此況):地圖過去只回 triplet_count,AI 讀到 0 就誤判「這庫沒有知識」,
|
||||
// 但實際上 kbdb_search 找得到內容——因為 entries 一直都在,只是沒被萃取成三元組。
|
||||
const db = makeSqliteD1();
|
||||
await seedTripletTemplate(db);
|
||||
await ensureTripletLibrarySlot(db, 'triplet');
|
||||
await createEntry(db, {
|
||||
content: '人資資料 A',
|
||||
entry_type: 'block',
|
||||
owner_id: 'leo',
|
||||
metadata_json: JSON.stringify({ library: 'hr' }),
|
||||
});
|
||||
await createEntry(db, {
|
||||
content: '人資資料 B',
|
||||
entry_type: 'block',
|
||||
owner_id: 'leo',
|
||||
metadata_json: JSON.stringify({ library: 'hr' }),
|
||||
});
|
||||
const { app, env } = makeApp(db);
|
||||
|
||||
// 單庫詳圖:triplet_count 仍是 0(沒騙這件事),但 entry_count 誠實回 2。
|
||||
const detailRes = await app.request('/map/hr?owner_id=leo', {}, env);
|
||||
const detailBody = (await detailRes.json()) as { map: { triplet_count: number; entry_count: number } };
|
||||
expect(detailBody.map.triplet_count).toBe(0);
|
||||
expect(detailBody.map.entry_count).toBe(2);
|
||||
|
||||
// 全館視圖:同一個庫、同一組數字,兩個 consumer(GET /map、GET /map/:library)不能對不上。
|
||||
const listRes = await app.request('/map?owner_id=leo', {}, env);
|
||||
const listBody = (await listRes.json()) as { libraries: { library: string; triplet_count: number; entry_count: number }[] };
|
||||
const hr = listBody.libraries.find((l) => l.library === 'hr');
|
||||
expect(hr).toBeDefined();
|
||||
expect(hr!.triplet_count).toBe(0);
|
||||
expect(hr!.entry_count).toBe(2);
|
||||
});
|
||||
|
||||
it('entry_count 的計數口徑排除 value 型 entries 與地圖自己的歷史摘要 block(不自我膨脹)', async () => {
|
||||
// record-crud 建 triplet record 時,每個 slot 值都會落一筆 entry_type='value' 的 entry
|
||||
// (儲存實作細節,不是「一筆知識」);地圖 recompute 也會建 entry_type='block' 且
|
||||
// metadata.kind='library_map' 的摘要 entry——兩者都不該被算進 entry_count,否則地圖會把
|
||||
// 自己的內部管線雜訊當成「使用者知識」回報,數字沒有意義。
|
||||
const db = makeSqliteD1();
|
||||
await seedTripletTemplate(db);
|
||||
await ensureTripletLibrarySlot(db, 'triplet');
|
||||
// 建一條 kb 三元組 → 連帶產生數筆 entry_type='value' 的 entries(不該被算進 entry_count)。
|
||||
await seedTriplet(db, { s: 'A', p: '連結至', o: 'B', library: 'kb' });
|
||||
// 觸發一次 recompute → 產生一筆 entry_type='block'/metadata.kind='library_map' 的摘要 entry
|
||||
// (同樣不該被算進 entry_count)。
|
||||
await recomputeLibraryMap(db, { library: 'kb' });
|
||||
|
||||
const detail = await getLibraryMapDetail(db, 'kb');
|
||||
// 這個情境下 kb 沒有任何「真的 ingest 進來的原始內容」entry(只有 value 碎片與地圖自己的摘要),
|
||||
// entry_count 應誠實回 0——不能因為底層 entries 表其實有好幾筆就報出一個誤導的非零數字。
|
||||
expect(detail!.entry_count).toBe(0);
|
||||
expect(detail!.triplet_count).toBe(1); // 對照:triplet_count 不受這個排除規則影響,維持原樣。
|
||||
});
|
||||
|
||||
it('owner 隔離:即時新鮮度層不會把別的 owner 的三元組算進來', async () => {
|
||||
const db = makeSqliteD1();
|
||||
await seedTripletTemplate(db);
|
||||
|
||||
@@ -27,6 +27,10 @@ export interface LibraryMapRow {
|
||||
narrative: string | null;
|
||||
top_entities: unknown; // 正常是 string[];防禦:舊部署可能回 JSON 字串形
|
||||
triplet_count: number | string;
|
||||
// Arcrun#87 三次收尾(2026-08-13,「藏書地圖說實話」):triplet_count=0 不等於這庫沒有知識——
|
||||
// 可能只是三元組萃取從沒對它跑過,但 entries(原始 ingest 內容)還在,kbdb_search 找得到。
|
||||
// 選填是因為舊部署(未帶這次修法)的 kbdb base 回應裡不會有這個欄位,容錯當 0。
|
||||
entry_count?: number | string;
|
||||
updated_at?: number;
|
||||
}
|
||||
|
||||
@@ -65,7 +69,15 @@ export function entityNames(raw: unknown, limit: number): string[] {
|
||||
const MAX_LIBRARY_LINES = 30;
|
||||
const MAX_NARRATIVE_CHARS = 60;
|
||||
|
||||
/** 把 GET /map 的 libraries[] 渲染成緊湊文字(每庫一行,design §4 指定格式)。空清單回 null。 */
|
||||
/**
|
||||
* 把 GET /map 的 libraries[] 渲染成緊湊文字(每庫一行,design §4 指定格式)。空清單回 null。
|
||||
*
|
||||
* Arcrun#87 三次收尾(2026-08-13):這是「開場那份地圖」的真身——一個全新 session 連上 MCP,
|
||||
* 這段文字就是它對藏書地圖的第一印象。過去只印 triplet_count,kb 以外幾乎全庫顯示「0 triplets」,
|
||||
* 讀起來像「這些庫沒有知識」,於是 session 直接跳過不查——但那 7 個庫的 entries(原始 ingest
|
||||
* 內容)其實都在,只是三元組萃取沒對它們跑過,kbdb_search 找得到真答案。現在 triplet_count=0
|
||||
* 且 entry_count>0 時明講「有內容、只是還沒萃取」,別再讓這句話變成瞎猜的理由。
|
||||
*/
|
||||
export function renderLibraryMapLines(libraries: LibraryMapRow[]): string | null {
|
||||
const rows = libraries.filter((l) => l && typeof l.library === "string" && l.library);
|
||||
if (rows.length === 0) return null;
|
||||
@@ -74,8 +86,15 @@ export function renderLibraryMapLines(libraries: LibraryMapRow[]): string | null
|
||||
const clipped =
|
||||
narrative.length > MAX_NARRATIVE_CHARS ? `${narrative.slice(0, MAX_NARRATIVE_CHARS)}…` : narrative;
|
||||
const core = entityNames(l.top_entities, 3);
|
||||
const count = Number(l.triplet_count ?? 0) || 0;
|
||||
return `- ${l.library}:${clipped}|核心:${core.length ? core.join("、") : "(尚無)"}|${count} triplets`;
|
||||
const tripletCount = Number(l.triplet_count ?? 0) || 0;
|
||||
const entryCount = Number(l.entry_count ?? 0) || 0;
|
||||
const countPart =
|
||||
tripletCount > 0
|
||||
? `${tripletCount} triplets`
|
||||
: entryCount > 0
|
||||
? `0 triplets/${entryCount} 筆原始內容(尚未萃取關係,kbdb_search 查得到)`
|
||||
: `0 triplets/0 內容`;
|
||||
return `- ${l.library}:${clipped}|核心:${core.length ? core.join("、") : "(尚無)"}|${countPart}`;
|
||||
});
|
||||
const omitted = rows.length > MAX_LIBRARY_LINES ? `\n(其餘 ${rows.length - MAX_LIBRARY_LINES} 庫略,kbdb_get_map 可看全部)` : "";
|
||||
return lines.join("\n") + omitted;
|
||||
|
||||
@@ -5,13 +5,20 @@
|
||||
* MCP 只做介面轉換——經既有 KBDB service binding(kbdbFetch)打 GET /map//map/:library,
|
||||
* 不碰 D1、不新增 binding。與 #68 kbdb_graph_neighbors 同族(D17 KBDB MCP 面,kbdb_* 前綴)。
|
||||
*
|
||||
* 端點契約(kbdb/src/routes/map.ts,M2 已 merge):
|
||||
* 端點契約(kbdb/src/routes/map.ts,M2 已 merge;entry_count 為 Arcrun#87 三次收尾新增):
|
||||
* GET /map → { success, libraries:[{library, narrative, top_entities(名字 top3),
|
||||
* triplet_count, updated_at}], count }
|
||||
* triplet_count, entry_count, updated_at}], count }
|
||||
* GET /map/:library → { success, map:{record_id, library, narrative, content, top_entities,
|
||||
* relation_profile, bridges, triplet_count, commit_hash, status, updated_at} }
|
||||
* relation_profile, bridges, triplet_count, entry_count, commit_hash,
|
||||
* status, updated_at} }
|
||||
* 404 → { success:false, error:'not found' }(該庫從未 recompute)
|
||||
*
|
||||
* entry_count vs triplet_count(讀這份地圖前務必分清楚,否則會誤判某庫「沒有知識」):
|
||||
* triplet_count=萃取出的三元組數;entry_count=ingest 進來的原始內容(卡片/skill/block)數。
|
||||
* 兩者不同源,一個 0 一個非 0 是正常狀態——triplet_count=0 只代表「還沒萃取關係」,
|
||||
* 不代表「沒有資料」。entry_count>0 時該用 kbdb_search 查內容,不要因為 triplet_count=0
|
||||
* 就跳過這個庫。
|
||||
*
|
||||
* slot 值防禦:top_entities/relation_profile/bridges 底層存 JSON 字串,正常 base 已 parse;
|
||||
* 但仍容錯「字串形直出」(live 曾觀測)——字串就 parse、失敗當空陣列,絕不 crash(鐵律)。
|
||||
*/
|
||||
@@ -59,11 +66,23 @@ interface LibraryMapDetail {
|
||||
relation_profile?: unknown;
|
||||
bridges?: unknown;
|
||||
triplet_count?: number | string;
|
||||
// Arcrun#87 三次收尾:同 lib/library-map.ts LibraryMapRow.entry_count 的欄位(見該檔註解)。
|
||||
entry_count?: number | string;
|
||||
commit_hash?: string | null;
|
||||
status?: string;
|
||||
updated_at?: number;
|
||||
}
|
||||
|
||||
/** triplet_count=0 但 entry_count>0 時給的提示(別讓「0 triplets」被讀成「這庫沒有知識」)。 */
|
||||
function entryOnlyHint(tripletCount: number, entryCount: number, library?: string): string[] {
|
||||
if (tripletCount > 0 || entryCount <= 0) return [];
|
||||
const lib = library ? `「${library}」` : "這個庫";
|
||||
return [
|
||||
`${lib} triplet_count=0,但有 ${entryCount} 筆原始內容(entries)——三元組萃取還沒對它跑過,` +
|
||||
"不代表沒有知識。用 kbdb_search(關鍵字或語義)直接查得到內容。",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* kbdb_get_map — 藏書地圖。無參數=全館(每庫一行);帶 library=該庫詳圖。
|
||||
* design §6 retrieval 流程的第一站:地圖 → get_map(library) 細節 → graph/search 進庫。
|
||||
@@ -113,6 +132,7 @@ export function registerGetMap(server: McpServer, env: Env, identity: KnowledgeI
|
||||
// 防禦:top_entities 若是 JSON 字串形就 parse 成名字清單(失敗當空,誠實不 crash)。
|
||||
top_entities: entityNames(l.top_entities, 3),
|
||||
triplet_count: Number(l.triplet_count ?? 0) || 0,
|
||||
entry_count: Number(l.entry_count ?? 0) || 0,
|
||||
}));
|
||||
if (libraries.length === 0) {
|
||||
// 空庫誠實回報:不是錯誤(端點正常)。地圖是讀時即時核對重算的(見 RECOMPUTE_HINTS
|
||||
@@ -128,9 +148,21 @@ export function registerGetMap(server: McpServer, env: Env, identity: KnowledgeI
|
||||
...RECOMPUTE_HINTS,
|
||||
]);
|
||||
}
|
||||
// Arcrun#87 三次收尾:某些庫 triplet_count=0 但 entry_count>0(entries 有、三元組
|
||||
// 萃取沒跑過)——這件事只在「這種庫真的存在」時才提一次,不是每個庫都印一行(避免洗版),
|
||||
// 讓讀這份地圖的 session 知道「觸目所及的 0」不能直接當成「沒有知識」。
|
||||
const entryOnlyLibs = libraries.filter(
|
||||
(l) => (l.triplet_count ?? 0) === 0 && (l.entry_count ?? 0) > 0,
|
||||
);
|
||||
return successResponse({ libraries, count: libraries.length }, [
|
||||
"要看某庫細節:kbdb_get_map(library='庫名')",
|
||||
"進庫查內容:kbdb_search(關鍵字/語義);查關係:kbdb_graph_neighbors",
|
||||
...(entryOnlyLibs.length > 0
|
||||
? [
|
||||
`${entryOnlyLibs.map((l) => l.library).join("、")} 這幾庫 triplet_count=0 但 entry_count>0:` +
|
||||
"有原始內容,只是還沒萃取出三元組關係——別把 0 triplets 讀成「沒有知識」,直接 kbdb_search 進去查。",
|
||||
]
|
||||
: []),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -167,10 +199,12 @@ export function registerGetMap(server: McpServer, env: Env, identity: KnowledgeI
|
||||
relation_profile: parseSlotArray<{ predicate: string; count: number }>(raw.relation_profile),
|
||||
bridges: parseSlotArray<{ entity: string; libraries: string[] }>(raw.bridges),
|
||||
triplet_count: Number(raw.triplet_count ?? 0) || 0,
|
||||
entry_count: Number(raw.entry_count ?? 0) || 0,
|
||||
};
|
||||
return successResponse({ map }, [
|
||||
"bridges=此庫 entity 同時出現在哪些其他庫(只有兩側三元組都標了 library 值才抓得到,舊資料若沒標會偏稀疏,是誠實現況不是 bug)",
|
||||
"沿核心 entity 挖關係:kbdb_graph_neighbors(subject=entity 名)",
|
||||
...entryOnlyHint(map.triplet_count, map.entry_count, library),
|
||||
]);
|
||||
} catch (e) {
|
||||
return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["稍後重試"]);
|
||||
|
||||
@@ -121,6 +121,46 @@ describe("kbdb_get_map: 全館地圖(無參數)", () => {
|
||||
expect(calls[0].url.searchParams.get("owner_id")).toBe("leo");
|
||||
});
|
||||
|
||||
it("Arcrun#87 三次收尾:triplet_count=0 但 entry_count>0 的庫附上「別當成沒有知識」的提示", async () => {
|
||||
// leo21c 實測現況:kb 以外幾乎每庫都長這樣——entries 有(且 library 標對),三元組萃取沒跑過。
|
||||
const emptyTripletRow = {
|
||||
library: "arcrun",
|
||||
narrative: null,
|
||||
top_entities: [],
|
||||
triplet_count: 0,
|
||||
entry_count: 15,
|
||||
updated_at: 1786330534,
|
||||
};
|
||||
const { server, tools } = makeServer();
|
||||
const { env } = makeEnv(
|
||||
() => new Response(JSON.stringify({ success: true, libraries: [emptyTripletRow], count: 1 })),
|
||||
);
|
||||
registerGetMap(server, env, SERVICE);
|
||||
const res = await tools.get("kbdb_get_map")!.handler({});
|
||||
const body = parseResult(res);
|
||||
const data = body.data as { libraries: { library: string; triplet_count: number; entry_count: number }[] };
|
||||
expect(data.libraries[0].entry_count).toBe(15);
|
||||
expect(data.libraries[0].triplet_count).toBe(0);
|
||||
// 提示要點名該庫、講清楚「有內容只是沒萃取」,別讓 AI 讀到 0 triplets 就跳過這個庫。
|
||||
const hints = (body.hints as string[]).join(" ");
|
||||
expect(hints).toContain("arcrun");
|
||||
expect(hints).toContain("entry_count");
|
||||
expect(hints).toContain("kbdb_search");
|
||||
});
|
||||
|
||||
it("entry_count 缺席(舊部署未帶此欄位)→ 容錯當 0,不 crash、不誤發提示", async () => {
|
||||
const legacyRow = { ...KB_ROW }; // KB_ROW 本身沒有 entry_count 欄位(模擬舊部署回應)
|
||||
const { server, tools } = makeServer();
|
||||
const { env } = makeEnv(
|
||||
() => new Response(JSON.stringify({ success: true, libraries: [legacyRow], count: 1 })),
|
||||
);
|
||||
registerGetMap(server, env, SERVICE);
|
||||
const res = await tools.get("kbdb_get_map")!.handler({});
|
||||
const body = parseResult(res);
|
||||
const data = body.data as { libraries: { entry_count: number }[] };
|
||||
expect(data.libraries[0].entry_count).toBe(0);
|
||||
});
|
||||
|
||||
it("top_entities in JSON-string form is parsed defensively (live-observed slot shape)", async () => {
|
||||
const { server, tools } = makeServer();
|
||||
const row = {
|
||||
@@ -213,6 +253,39 @@ describe("kbdb_get_map: 單庫詳圖(library 參數)", () => {
|
||||
expect(map.bridges).toEqual([{ entity: "Gitea", libraries: ["notes"] }]);
|
||||
});
|
||||
|
||||
it("Arcrun#87 三次收尾:單庫詳圖 triplet_count=0、entry_count>0 → 附「別當空庫」提示", async () => {
|
||||
const emptyTripletDetail = {
|
||||
...DETAIL,
|
||||
library: "arcrun",
|
||||
triplet_count: 0,
|
||||
entry_count: 15,
|
||||
top_entities: [],
|
||||
relation_profile: [],
|
||||
bridges: [],
|
||||
};
|
||||
const { server, tools } = makeServer();
|
||||
const { env } = makeEnv(() => new Response(JSON.stringify({ success: true, map: emptyTripletDetail })));
|
||||
registerGetMap(server, env, SERVICE);
|
||||
const res = await tools.get("kbdb_get_map")!.handler({ library: "arcrun" });
|
||||
const body = parseResult(res);
|
||||
const map = (body.data as { map: { entry_count: number; triplet_count: number } }).map;
|
||||
expect(map.triplet_count).toBe(0);
|
||||
expect(map.entry_count).toBe(15);
|
||||
const hints = (body.hints as string[]).join(" ");
|
||||
expect(hints).toContain("arcrun");
|
||||
expect(hints).toContain("kbdb_search");
|
||||
});
|
||||
|
||||
it("triplet_count>0 的庫不附「別當空庫」提示(不需要時別洗版)", async () => {
|
||||
const { server, tools } = makeServer();
|
||||
const { env } = makeEnv(() => new Response(JSON.stringify({ success: true, map: DETAIL }))); // DETAIL.triplet_count=111
|
||||
registerGetMap(server, env, SERVICE);
|
||||
const res = await tools.get("kbdb_get_map")!.handler({ library: "kb" });
|
||||
const body = parseResult(res);
|
||||
const hints = (body.hints as string[]).join(" ");
|
||||
expect(hints).not.toContain("entry_count");
|
||||
});
|
||||
|
||||
it("JSON-string slot values are parsed into objects(任務規格); broken JSON → 空陣列", async () => {
|
||||
const { server, tools } = makeServer();
|
||||
const raw = {
|
||||
@@ -343,6 +416,29 @@ describe("renderLibraryMapLines", () => {
|
||||
it("empty input → null", () => {
|
||||
expect(renderLibraryMapLines([])).toBeNull();
|
||||
});
|
||||
|
||||
it("Arcrun#87 三次收尾:triplet_count=0 但 entry_count>0 → 開場那行不再讀成「這庫沒有知識」", () => {
|
||||
// 這是 leo21c 的實際現況(kb 以外 7 庫皆此況)——這段渲染出的文字是全新 session 連上 MCP
|
||||
// 第一眼看到的地圖,過去只印「0 triplets」,讀起來像空庫,session 因此跳過不查。
|
||||
const text = renderLibraryMapLines([
|
||||
{ library: "arcrun", narrative: null, top_entities: [], triplet_count: 0, entry_count: 15 },
|
||||
]);
|
||||
expect(text).toBe("- arcrun:(narrative 待補)|核心:(尚無)|0 triplets/15 筆原始內容(尚未萃取關係,kbdb_search 查得到)");
|
||||
});
|
||||
|
||||
it("triplet_count=0 且 entry_count=0(真的沒有任何內容)→ 誠實講兩個都是 0", () => {
|
||||
const text = renderLibraryMapLines([
|
||||
{ library: "empty-lib", narrative: null, top_entities: [], triplet_count: 0, entry_count: 0 },
|
||||
]);
|
||||
expect(text).toBe("- empty-lib:(narrative 待補)|核心:(尚無)|0 triplets/0 內容");
|
||||
});
|
||||
|
||||
it("entry_count 缺席(舊部署未帶欄位)→ 容錯當 0,維持既有 triplet-only 措辭不 crash", () => {
|
||||
const text = renderLibraryMapLines([
|
||||
{ library: "kb", narrative: "摘要", top_entities: ["A"], triplet_count: 5 },
|
||||
]);
|
||||
expect(text).toBe("- kb:摘要|核心:A|5 triplets");
|
||||
});
|
||||
});
|
||||
|
||||
// ── 2026-08-12:地圖也要跟著登入者的權限走 ────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user