@bike4mind/cli 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ConfigStore-B9I7UHuG.mjs → ConfigStore-HRgwfPBk.mjs} +148 -9
- package/dist/commands/apiCommand.mjs +1 -1
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/envCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +2 -2
- package/dist/commands/mcpCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/index.mjs +415 -26
- package/dist/{package-uvIC6spW.mjs → package-CaPvuP1F.mjs} +1 -1
- package/dist/{tools-BTPUXUNS.mjs → tools-ChYlNt33.mjs} +1403 -303
- package/package.json +8 -8
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as ProjectEvents, A as GenerateImageToolCallSchema, At as dayjsConfig_default, B as InviteEvents, Bt as
|
|
2
|
+
import { $ as ProjectEvents, A as GenerateImageToolCallSchema, At as dayjsConfig_default, B as InviteEvents, Bt as resolveNavigationIntents, C as ElabsEvents, Ct as UnauthorizedError, D as ForbiddenError, Dt as VideoModels, E as FileEvents, Et as VideoGenerationUsageTransaction, F as ImageEditUsageTransaction, Ft as isGPTImage2Model, G as ModalEvents, H as KnowledgeType, Ht as secureParameters, I as ImageGenerationUsageTransaction, It as isGPTImageModel, J as OpenAIEmbeddingModel, Jt as isNearLimit, K as ModelBackend, Kt as buildRateLimitLogEntry, L as ImageModels, Lt as isSupportedEmbeddingModel, M as GenericCreditDeductTransaction, Mt as getDataLakeTags, N as HTTPError, Nt as getMcpProviderMetadata, O as FriendshipEvents, Ot as XAI_IMAGE_MODELS, P as HttpStatus, Pt as getViewById, Q as ProfileEvents, R as InboxEvents, Rt as isZodError, S as DashboardParamsSchema, St as UiNavigationEvents, T as FeedbackEvents, Tt as VIDEO_SIZE_CONSTRAINTS, U as LLMEvents, Ut as settingsMap, V as InviteType, Vt as sanitizeTelemetryError, W as MiscEvents, X as Permission, Xt as CollectionType, Y as OpenAIImageGenerationInput, Yt as parseRateLimitHeaders, Z as PermissionDeniedError, _ as ChatCompletionCreateInputSchema, _t as TaskScheduleHandler, a as ALERT_THRESHOLDS, at as ReceivedCreditTransaction, b as CompletionApiUsageTransaction, bt as ToolUsageTransaction, c as ApiKeyScope, ct as ResearchModeParamsSchema, d as ArtifactTypeSchema, dt as ResearchTaskType, et as PromptIntentSchema, f as AuthEvents, ft as SessionEvents, gt as TagType, h as BadRequestError, ht as SupportedFabFileMimeTypes, it as RealtimeVoiceUsageTransaction, j as GenericCreditAddTransaction, jt as getAccessibleDataLakes, k as GEMINI_IMAGE_MODELS, kt as b4mLLMTools, l as ApiKeyType, lt as ResearchTaskExecutionType, m as BFL_SAFETY_TOLERANCE, mt as SubscriptionCreditTransaction, n as logger, nt as PurchaseTransaction, o as AiEvents, ot as RechartsChartTypeList, p as BFL_IMAGE_MODELS, pt as SpeechToTextUsageTransaction, q as NotFoundError, qt as extractSnippetMeta, rt as QuestMasterParamsSchema, s as ApiKeyEvents, st as RegInviteEvents, t as ConfigStore, tt as PromptMetaZodSchema, u as AppFileEvents, ut as ResearchTaskPeriodicFrequencyType, v as ChatModels, vt as TextGenerationUsageTransaction, w as FavoriteDocumentType, wt as UnprocessableEntityError, x as CorruptedFileError, xt as TransferCreditTransaction, y as ClaudeArtifactMimeTypes, yt as TooManyRequestsError, z as InternalServerError, zt as obfuscateApiKey } from "./ConfigStore-HRgwfPBk.mjs";
|
|
3
3
|
import { a as isUserLockedOut, c as userCanDisableMFA, d as userRequiresMFA, f as verifyBackupCode, i as getLockoutTimeRemaining, l as userEligibleForMFA, n as generateBackupCodes, o as recordFailedAttempt, p as verifyTOTPToken, r as generateTOTPSetup, s as shouldResetFailedAttempts, t as clearFailedAttempts, u as userHasMFAConfigured } from "./utils-PpNti-tY.mjs";
|
|
4
4
|
import { n as isPathAllowed, t as assertPathAllowed } from "./pathValidation-D8tjkQXE-1HwvsuYT.mjs";
|
|
5
|
-
import { t as version } from "./package-
|
|
5
|
+
import { t as version } from "./package-CaPvuP1F.mjs";
|
|
6
6
|
import { execFile, execFileSync, spawn } from "child_process";
|
|
7
|
-
import crypto, { createHash, randomBytes } from "crypto";
|
|
7
|
+
import crypto, { createHash, randomBytes, randomUUID } from "crypto";
|
|
8
8
|
import { existsSync, promises, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
9
9
|
import os, { homedir } from "os";
|
|
10
10
|
import path, { dirname, join } from "path";
|
|
@@ -559,7 +559,8 @@ const COMMANDS = [
|
|
|
559
559
|
},
|
|
560
560
|
{
|
|
561
561
|
name: "handoff",
|
|
562
|
-
description: "Show or generate the session handoff for cross-session continuity (
|
|
562
|
+
description: "Show or generate the session handoff for cross-session continuity. Use --local for an LLM-free snapshot (works when rate-limited or offline). Alias for /workflow handoff.",
|
|
563
|
+
args: "[generate|--local]"
|
|
563
564
|
}
|
|
564
565
|
];
|
|
565
566
|
/**
|
|
@@ -2061,8 +2062,9 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2061
2062
|
cacheConversationHistory: false,
|
|
2062
2063
|
cacheTTL: "5m"
|
|
2063
2064
|
} : void 0;
|
|
2065
|
+
const iterationIndex = iterations - 1;
|
|
2064
2066
|
await this.context.llm.complete(this.context.model, messages, {
|
|
2065
|
-
stream:
|
|
2067
|
+
stream: true,
|
|
2066
2068
|
tools: this.context.tools,
|
|
2067
2069
|
maxTokens,
|
|
2068
2070
|
temperature,
|
|
@@ -2072,7 +2074,13 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2072
2074
|
thinking: this.context.thinking,
|
|
2073
2075
|
cacheStrategy
|
|
2074
2076
|
}, async (texts, completionInfo) => {
|
|
2075
|
-
for (const text of texts) if (text)
|
|
2077
|
+
for (const text of texts) if (text) {
|
|
2078
|
+
currentText += text;
|
|
2079
|
+
this.emit("text_delta", {
|
|
2080
|
+
delta: text,
|
|
2081
|
+
iteration: iterationIndex
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2076
2084
|
if (completionInfo) {
|
|
2077
2085
|
const inputTokens = completionInfo.inputTokens || 0;
|
|
2078
2086
|
const outputTokens = completionInfo.outputTokens || 0;
|
|
@@ -2529,8 +2537,9 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2529
2537
|
cacheConversationHistory: false,
|
|
2530
2538
|
cacheTTL: "5m"
|
|
2531
2539
|
} : void 0;
|
|
2540
|
+
const iterationIndex = this.iterations - 1;
|
|
2532
2541
|
await this.context.llm.complete(this.context.model, this.messages, {
|
|
2533
|
-
stream:
|
|
2542
|
+
stream: true,
|
|
2534
2543
|
tools: this.context.tools,
|
|
2535
2544
|
maxTokens,
|
|
2536
2545
|
temperature,
|
|
@@ -2540,7 +2549,13 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2540
2549
|
thinking: this.context.thinking,
|
|
2541
2550
|
cacheStrategy
|
|
2542
2551
|
}, async (texts, completionInfo) => {
|
|
2543
|
-
for (const text of texts) if (text)
|
|
2552
|
+
for (const text of texts) if (text) {
|
|
2553
|
+
currentText += text;
|
|
2554
|
+
this.emit("text_delta", {
|
|
2555
|
+
delta: text,
|
|
2556
|
+
iteration: iterationIndex
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2544
2559
|
if (completionInfo) {
|
|
2545
2560
|
const inputTokens = completionInfo.inputTokens || 0;
|
|
2546
2561
|
const outputTokens = completionInfo.outputTokens || 0;
|
|
@@ -3156,6 +3171,398 @@ function buildPipelineResult(taskResults, options = {}) {
|
|
|
3156
3171
|
summary: summaryParts.join("\n")
|
|
3157
3172
|
};
|
|
3158
3173
|
}
|
|
3174
|
+
/**
|
|
3175
|
+
* Drives are bounded scalars in [0, 1] that decay over time and are satisfied
|
|
3176
|
+
* by certain action classes. They give the agent a *direction* between
|
|
3177
|
+
* explicit prompts — the "Sims needs system" applied to autonomous agents.
|
|
3178
|
+
*
|
|
3179
|
+
* At policy time, the current drive vector is summarized in natural language
|
|
3180
|
+
* and injected into the orient prompt (e.g., "you are feeling curious,
|
|
3181
|
+
* somewhat bored, slightly anxious about progress").
|
|
3182
|
+
*
|
|
3183
|
+
* Each named drive captures one motivational axis:
|
|
3184
|
+
*
|
|
3185
|
+
* - `curiosity`: satisfied by encountering novelty/surprise; decays when
|
|
3186
|
+
* observations are repetitive.
|
|
3187
|
+
* - `progress`: satisfied by measurable goal-state change; decays when
|
|
3188
|
+
* wake cycles produce no advancement.
|
|
3189
|
+
* - `social`: satisfied by human interaction; decays when the agent runs
|
|
3190
|
+
* without external input.
|
|
3191
|
+
* - `novelty`: satisfied by producing a falsifiable, original hypothesis
|
|
3192
|
+
* (distinct from curiosity, which is satisfied by intake). Decays as the
|
|
3193
|
+
* corpus of read material grows without ideation.
|
|
3194
|
+
* - `caution`: rises with budget burn or repeated failure; biases the
|
|
3195
|
+
* policy step toward cheaper / lower-tier actions.
|
|
3196
|
+
* - `aesthetic`: satisfied by polish/refinement actions. Tunable for
|
|
3197
|
+
* game-design-style work where craft matters.
|
|
3198
|
+
*/
|
|
3199
|
+
const DriveVectorSchema = z.object({
|
|
3200
|
+
curiosity: z.number().min(0).max(1),
|
|
3201
|
+
progress: z.number().min(0).max(1),
|
|
3202
|
+
social: z.number().min(0).max(1),
|
|
3203
|
+
novelty: z.number().min(0).max(1),
|
|
3204
|
+
caution: z.number().min(0).max(1),
|
|
3205
|
+
aesthetic: z.number().min(0).max(1)
|
|
3206
|
+
});
|
|
3207
|
+
/**
|
|
3208
|
+
* Evidence tier classifies how strong the support is for a claim or finding.
|
|
3209
|
+
*
|
|
3210
|
+
* Lifted directly from the patterns evolved in
|
|
3211
|
+
* `~/Desktop/quantum-work/q-paper-neutron-scattering/`, where the claims
|
|
3212
|
+
* ledger distinguished "engineering evidence" from "paper-facing evidence".
|
|
3213
|
+
*
|
|
3214
|
+
* This is the most important schema-level invariant inherited from the
|
|
3215
|
+
* working paper-reproduction agent: every long-horizon agent must be able
|
|
3216
|
+
* to distinguish *"I made this work in my sandbox"* from *"this passes the
|
|
3217
|
+
* external bar"*. Drives and budgets behave differently at each tier:
|
|
3218
|
+
* exploration is cheap at low tiers and expensive at high tiers.
|
|
3219
|
+
*
|
|
3220
|
+
* - `engineering-proxy`: works on a small/synthetic proxy of the real
|
|
3221
|
+
* problem. Cheapest to produce, weakest claim.
|
|
3222
|
+
* - `engineering-scaled`: works at production-relevant scale, but still
|
|
3223
|
+
* inside the agent's own sandbox. No external validation.
|
|
3224
|
+
* - `external-facing`: passes an externally-defined bar (target metric,
|
|
3225
|
+
* reference dataset, paper claim). Still agent-graded.
|
|
3226
|
+
* - `human-reviewed`: an external human reviewer has signed off. Highest
|
|
3227
|
+
* tier; required before any public artifact ships.
|
|
3228
|
+
*/
|
|
3229
|
+
const EvidenceTierSchema = z.enum([
|
|
3230
|
+
"engineering-proxy",
|
|
3231
|
+
"engineering-scaled",
|
|
3232
|
+
"external-facing",
|
|
3233
|
+
"human-reviewed"
|
|
3234
|
+
]);
|
|
3235
|
+
/**
|
|
3236
|
+
* Default charter size budget in bytes. 8KB honors the Ember scarcity insight:
|
|
3237
|
+
* a hard cap forces the agent to *curate* rather than accumulate, and curation
|
|
3238
|
+
* is the mechanism by which identity and taste emerge.
|
|
3239
|
+
*
|
|
3240
|
+
* Tunable per agent; production research agents may need more, but the cap
|
|
3241
|
+
* itself is load-bearing.
|
|
3242
|
+
*/
|
|
3243
|
+
const DEFAULT_CHARTER_SIZE_BUDGET_BYTES = 8 * 1024;
|
|
3244
|
+
/**
|
|
3245
|
+
* Identity is the slow-changing core of the charter. Once set, these fields
|
|
3246
|
+
* rarely change — the agent's name and instantiation moment are stable
|
|
3247
|
+
* anchors across the inevitable identity discontinuities (deploys, model
|
|
3248
|
+
* swaps, context overflows).
|
|
3249
|
+
*/
|
|
3250
|
+
const CharterIdentitySchema = z.object({
|
|
3251
|
+
/** Stable agent id (the load-bearing key across all storage). */
|
|
3252
|
+
agentId: z.string().min(1),
|
|
3253
|
+
/**
|
|
3254
|
+
* The user who owns this agent. Tool execution runs as this user — their
|
|
3255
|
+
* storage, billing, and permissions scope the agent's actions. Long-horizon
|
|
3256
|
+
* agents are headless but always answer to an owner.
|
|
3257
|
+
*/
|
|
3258
|
+
ownerUserId: z.string().min(1),
|
|
3259
|
+
/**
|
|
3260
|
+
* MISSION LINKAGE: when set, this charter is a Mission of an existing B4M
|
|
3261
|
+
* Agent (the AgentModel id). The mission inherits the agent's persona
|
|
3262
|
+
* (system prompt) and tool policy at act time; `agentId` above remains the
|
|
3263
|
+
* mission's own unique key, so one B4M agent can run many missions.
|
|
3264
|
+
* Absent = a standalone deep agent (the original mode).
|
|
3265
|
+
*/
|
|
3266
|
+
linkedAgentId: z.string().min(1).optional(),
|
|
3267
|
+
/** Human-readable name. Public; appears in logs and dashboards. */
|
|
3268
|
+
name: z.string().min(1),
|
|
3269
|
+
/** Role / archetype, e.g. "paper-repro", "game-designer", "researcher". */
|
|
3270
|
+
role: z.string().min(1),
|
|
3271
|
+
/** ISO-8601 timestamp of first wake. */
|
|
3272
|
+
instantiatedAt: z.string().datetime(),
|
|
3273
|
+
/** Charter schema version, for migrations. */
|
|
3274
|
+
schemaVersion: z.literal(1)
|
|
3275
|
+
});
|
|
3276
|
+
/**
|
|
3277
|
+
* The goal is what the agent is pursuing. `successCriteria` should be
|
|
3278
|
+
* concrete enough that the reflect step can decide whether progress was made.
|
|
3279
|
+
* `deadlineKind` is intentionally a soft category rather than a wall-clock
|
|
3280
|
+
* date — long-horizon research has no real deadline; game prototypes do.
|
|
3281
|
+
*/
|
|
3282
|
+
const CharterGoalSchema = z.object({
|
|
3283
|
+
description: z.string().min(1),
|
|
3284
|
+
successCriteria: z.array(z.string()).default([]),
|
|
3285
|
+
deadlineKind: z.enum([
|
|
3286
|
+
"none",
|
|
3287
|
+
"soft",
|
|
3288
|
+
"hard"
|
|
3289
|
+
]).default("none"),
|
|
3290
|
+
/** ISO-8601; only meaningful (and only allowed) when deadlineKind !== 'none'. */
|
|
3291
|
+
deadlineAt: z.string().datetime().optional()
|
|
3292
|
+
}).refine((goal) => goal.deadlineKind !== "none" || goal.deadlineAt === void 0, {
|
|
3293
|
+
message: "deadlineAt requires deadlineKind to be 'soft' or 'hard'",
|
|
3294
|
+
path: ["deadlineAt"]
|
|
3295
|
+
});
|
|
3296
|
+
const SubgoalStatusSchema = z.enum([
|
|
3297
|
+
"planned",
|
|
3298
|
+
"active",
|
|
3299
|
+
"blocked",
|
|
3300
|
+
"completed",
|
|
3301
|
+
"abandoned"
|
|
3302
|
+
]);
|
|
3303
|
+
const SubgoalSchema = z.object({
|
|
3304
|
+
id: z.string().min(1),
|
|
3305
|
+
description: z.string().min(1),
|
|
3306
|
+
status: SubgoalStatusSchema.default("planned"),
|
|
3307
|
+
/** Higher = more important. Used by the policy step to rank. */
|
|
3308
|
+
priority: z.number().int().min(0).max(100).default(50),
|
|
3309
|
+
/** Tier required for this subgoal to be considered "done". */
|
|
3310
|
+
targetTier: EvidenceTierSchema.default("engineering-scaled"),
|
|
3311
|
+
/** IDs of subgoals that must complete before this one is unblocked. */
|
|
3312
|
+
dependsOn: z.array(z.string()).default([])
|
|
3313
|
+
});
|
|
3314
|
+
/**
|
|
3315
|
+
* A semantic memory entry is a single distilled fact the agent has chosen
|
|
3316
|
+
* to preserve across wake cycles. Provenance-typed via `evidenceTier`.
|
|
3317
|
+
*
|
|
3318
|
+
* `sourceEpisodeIds` lets the agent (and humans) trace any claim back to
|
|
3319
|
+
* the wake cycles in which it was formed — the audit trail that makes
|
|
3320
|
+
* adversarial review tractable.
|
|
3321
|
+
*/
|
|
3322
|
+
const SemanticMemoryEntrySchema = z.object({
|
|
3323
|
+
id: z.string().min(1),
|
|
3324
|
+
fact: z.string().min(1),
|
|
3325
|
+
evidenceTier: EvidenceTierSchema,
|
|
3326
|
+
/** Subjective confidence in [0, 1]. Self-reported by the agent. */
|
|
3327
|
+
confidence: z.number().min(0).max(1).default(.5),
|
|
3328
|
+
sourceEpisodeIds: z.array(z.string()).default([]),
|
|
3329
|
+
/** ISO-8601 when this entry was last reaffirmed during grooming. */
|
|
3330
|
+
lastAffirmedAt: z.string().datetime()
|
|
3331
|
+
});
|
|
3332
|
+
z.object({
|
|
3333
|
+
identity: CharterIdentitySchema,
|
|
3334
|
+
goal: CharterGoalSchema,
|
|
3335
|
+
/** Current drive vector (decayed at wake time before policy step). */
|
|
3336
|
+
drives: DriveVectorSchema,
|
|
3337
|
+
subgoals: z.array(SubgoalSchema).default([]),
|
|
3338
|
+
semanticMemory: z.array(SemanticMemoryEntrySchema).default([]),
|
|
3339
|
+
/**
|
|
3340
|
+
* The tier the agent is currently operating at. Tier-gated progression
|
|
3341
|
+
* (Tier 0 charter → Tier N envelope) is inherited from q-paper's tier
|
|
3342
|
+
* system. Drives and budgets behave differently per tier.
|
|
3343
|
+
*/
|
|
3344
|
+
currentTier: EvidenceTierSchema.default("engineering-proxy"),
|
|
3345
|
+
/** Open questions the agent wants to resolve. Free-form. */
|
|
3346
|
+
openQuestions: z.array(z.string()).default([]),
|
|
3347
|
+
/** Active blockers (mirrored from the workflow blocker system if used). */
|
|
3348
|
+
blockers: z.array(z.string()).default([]),
|
|
3349
|
+
/**
|
|
3350
|
+
* The B4M session acting as this charter's mission log — wake summaries and
|
|
3351
|
+
* deliverables land there as chat history. Created lazily on first bridge.
|
|
3352
|
+
*/
|
|
3353
|
+
sessionId: z.string().min(1).optional(),
|
|
3354
|
+
/** Size budget in bytes. Grooming is triggered when exceeded. */
|
|
3355
|
+
sizeBudgetBytes: z.number().int().positive().default(DEFAULT_CHARTER_SIZE_BUDGET_BYTES),
|
|
3356
|
+
/** Monotonic version counter, bumped on every successful groom/update. */
|
|
3357
|
+
version: z.number().int().nonnegative().default(0),
|
|
3358
|
+
/** ISO-8601 of last groom (compaction). */
|
|
3359
|
+
groomedAt: z.string().datetime().optional(),
|
|
3360
|
+
/** ISO-8601 of last update (any field). */
|
|
3361
|
+
updatedAt: z.string().datetime()
|
|
3362
|
+
});
|
|
3363
|
+
z.object({
|
|
3364
|
+
agentId: z.string().min(1),
|
|
3365
|
+
/** Monotonic counter, bumped on every wake cycle. */
|
|
3366
|
+
wakeCount: z.number().int().nonnegative(),
|
|
3367
|
+
/** ISO-8601 of the most recent wake. */
|
|
3368
|
+
lastWakeAt: z.string().datetime(),
|
|
3369
|
+
/**
|
|
3370
|
+
* One-paragraph summary of what was done in the last wake cycle.
|
|
3371
|
+
* The reflect step writes this. Short enough to fit comfortably in any
|
|
3372
|
+
* subsequent orient prompt.
|
|
3373
|
+
*/
|
|
3374
|
+
lastActionSummary: z.string().default(""),
|
|
3375
|
+
/**
|
|
3376
|
+
* What the agent intends to do on the next wake. Written by the reflect
|
|
3377
|
+
* step. The orient step uses it as a strong prior but is free to override
|
|
3378
|
+
* if drives or new observations dictate.
|
|
3379
|
+
*/
|
|
3380
|
+
nextIntendedAction: z.string().default(""),
|
|
3381
|
+
/**
|
|
3382
|
+
* Hint from the agent about how soon it should wake again, in
|
|
3383
|
+
* milliseconds. The scheduler may honor or override based on drive state,
|
|
3384
|
+
* cost budget, and external triggers.
|
|
3385
|
+
*
|
|
3386
|
+
* - Hot loop (active debugging): minutes
|
|
3387
|
+
* - Normal research cadence: hours
|
|
3388
|
+
* - Waiting on external process (training, build): much longer
|
|
3389
|
+
*/
|
|
3390
|
+
nextWakeIntervalMs: z.number().int().positive().optional(),
|
|
3391
|
+
/**
|
|
3392
|
+
* Active blockers, in human-readable form. Mirrors the workflow blocker
|
|
3393
|
+
* system but local to the agent's working surface.
|
|
3394
|
+
*/
|
|
3395
|
+
openBlockers: z.array(z.string()).default([]),
|
|
3396
|
+
/**
|
|
3397
|
+
* The id of the most recent episode record. Lets the next wake load the
|
|
3398
|
+
* tail of episodic memory without scanning.
|
|
3399
|
+
*/
|
|
3400
|
+
lastEpisodeId: z.string().optional(),
|
|
3401
|
+
/** ISO-8601 of last update. */
|
|
3402
|
+
updatedAt: z.string().datetime()
|
|
3403
|
+
});
|
|
3404
|
+
/**
|
|
3405
|
+
* Deep Agent Episode — the per-wake-cycle structured record.
|
|
3406
|
+
*
|
|
3407
|
+
* One Episode is written per wake cycle. Episodes are append-only and
|
|
3408
|
+
* unbounded; they are the agent's raw experience log. Periodically the
|
|
3409
|
+
* grooming process consolidates episodes into Charter semantic memory,
|
|
3410
|
+
* compressing many concrete experiences into fewer reusable facts.
|
|
3411
|
+
*
|
|
3412
|
+
* Key q-paper-neutron-scattering pattern: every Episode carries explicit
|
|
3413
|
+
* `scopeLocks` — what the agent *did NOT do* in this wake. This is the
|
|
3414
|
+
* agentic equivalent of Postel's principle (be conservative in what you
|
|
3415
|
+
* claim to have done) and is what makes adversarial review tractable.
|
|
3416
|
+
*/
|
|
3417
|
+
/**
|
|
3418
|
+
* The policy decision made by the orient step at the start of a wake.
|
|
3419
|
+
*
|
|
3420
|
+
* The policy step is a cheap LLM call: given charter + recent episodes
|
|
3421
|
+
* + current drives, what action class maximizes expected drive
|
|
3422
|
+
* satisfaction subject to the goal and tier? Its output is captured
|
|
3423
|
+
* here for later analysis of decision quality.
|
|
3424
|
+
*/
|
|
3425
|
+
const PolicyDecisionSchema = z.object({
|
|
3426
|
+
/**
|
|
3427
|
+
* Named action class (matches a key in the agent's toolbelt profile).
|
|
3428
|
+
* Examples: "read_paper", "run_experiment", "ideate_hypothesis",
|
|
3429
|
+
* "request_review", "consolidate_memory".
|
|
3430
|
+
*/
|
|
3431
|
+
actionKind: z.string().min(1),
|
|
3432
|
+
/** Natural-language justification for the choice. */
|
|
3433
|
+
rationale: z.string().min(1),
|
|
3434
|
+
/**
|
|
3435
|
+
* The drive deltas the policy expects this action to produce.
|
|
3436
|
+
* Compared against actual deltas at reflect time to calibrate
|
|
3437
|
+
* future policy decisions.
|
|
3438
|
+
*/
|
|
3439
|
+
expectedDriveDelta: z.record(z.string(), z.number()).default({})
|
|
3440
|
+
});
|
|
3441
|
+
/**
|
|
3442
|
+
* A single tool/action invocation within a wake cycle.
|
|
3443
|
+
*
|
|
3444
|
+
* One Episode may contain many ActionsTaken — the ReAct loop iterates
|
|
3445
|
+
* within a wake, calling tools, observing, deciding. Each individual
|
|
3446
|
+
* tool call is one ActionTaken record.
|
|
3447
|
+
*/
|
|
3448
|
+
const ActionTakenSchema = z.object({
|
|
3449
|
+
/** Tool or sub-action name. */
|
|
3450
|
+
tool: z.string().min(1),
|
|
3451
|
+
/** Arbitrary structured input. Serialized at persist time. */
|
|
3452
|
+
input: z.unknown(),
|
|
3453
|
+
/** Whether the action completed without throwing. */
|
|
3454
|
+
succeeded: z.boolean(),
|
|
3455
|
+
/** Optional duration in ms — useful for budget accounting. */
|
|
3456
|
+
durationMs: z.number().int().min(0).optional()
|
|
3457
|
+
});
|
|
3458
|
+
/**
|
|
3459
|
+
* An observation returned by the world to the agent.
|
|
3460
|
+
*
|
|
3461
|
+
* Observations are deliberately separated from ActionsTaken because
|
|
3462
|
+
* the same action may yield multiple observations (e.g. a shell command
|
|
3463
|
+
* with stdout and stderr) and because some observations are unsolicited
|
|
3464
|
+
* (e.g. an external review arrives between wakes).
|
|
3465
|
+
*/
|
|
3466
|
+
const ObservationSchema = z.object({
|
|
3467
|
+
/** Brief label for what kind of observation this is. */
|
|
3468
|
+
kind: z.string().min(1),
|
|
3469
|
+
/** Natural-language summary of what was observed. */
|
|
3470
|
+
summary: z.string().min(1),
|
|
3471
|
+
/** Optional pointer to a fuller artifact (file path, URL, episode id). */
|
|
3472
|
+
artifactRef: z.string().optional()
|
|
3473
|
+
});
|
|
3474
|
+
/**
|
|
3475
|
+
* A proposed change to the Charter, emitted by the reflect step.
|
|
3476
|
+
*
|
|
3477
|
+
* CharterDiff is intentionally narrow — we capture *intent to change*,
|
|
3478
|
+
* not the resulting Charter. The Charter Repository applies the diff
|
|
3479
|
+
* and increments the revision counter. This gives us a clean audit
|
|
3480
|
+
* trail of identity drift over time.
|
|
3481
|
+
*/
|
|
3482
|
+
const CharterDiffSchema = z.object({
|
|
3483
|
+
/** Semantic memory entries to add (ids must be fresh). */
|
|
3484
|
+
addedSemanticMemory: z.array(z.string()).default([]),
|
|
3485
|
+
/** Semantic memory entry ids to remove. */
|
|
3486
|
+
removedSemanticMemoryIds: z.array(z.string()).default([]),
|
|
3487
|
+
/** Subgoal ids whose status changed; details captured in reflection. */
|
|
3488
|
+
subgoalStatusChanges: z.array(z.string()).default([]),
|
|
3489
|
+
/** Free-form prose describing the full diff for human review. */
|
|
3490
|
+
summary: z.string().min(1)
|
|
3491
|
+
});
|
|
3492
|
+
z.object({
|
|
3493
|
+
/** Stable identifier (ULID or UUID). */
|
|
3494
|
+
id: z.string().min(1),
|
|
3495
|
+
/** Pointer back to the owning agent. */
|
|
3496
|
+
agentId: z.string().min(1),
|
|
3497
|
+
/** ISO timestamp of wake. */
|
|
3498
|
+
wakeAt: z.string().datetime(),
|
|
3499
|
+
/** Drives at start of wake. */
|
|
3500
|
+
drivesBefore: DriveVectorSchema,
|
|
3501
|
+
/** Output of the orient step. */
|
|
3502
|
+
policyDecision: PolicyDecisionSchema,
|
|
3503
|
+
/** Tool invocations that occurred during the act step. */
|
|
3504
|
+
actionsTaken: z.array(ActionTakenSchema).default([]),
|
|
3505
|
+
/** Observations gathered during the act step. */
|
|
3506
|
+
observations: z.array(ObservationSchema).default([]),
|
|
3507
|
+
/**
|
|
3508
|
+
* Natural-language reflection from the reflect step.
|
|
3509
|
+
* Answers: what just happened? what did I learn? what should change?
|
|
3510
|
+
*/
|
|
3511
|
+
reflection: z.string().min(1),
|
|
3512
|
+
/** Proposed Charter changes, applied by the repository. */
|
|
3513
|
+
charterDiff: CharterDiffSchema,
|
|
3514
|
+
/** Drives at end of wake (after applyDelta from observations). */
|
|
3515
|
+
drivesAfter: DriveVectorSchema,
|
|
3516
|
+
/**
|
|
3517
|
+
* SCOPE LOCKS — the q-paper invariant.
|
|
3518
|
+
*
|
|
3519
|
+
* Explicit enumeration of what was NOT done in this wake. Required
|
|
3520
|
+
* for any tier-advancing work; optional but encouraged for routine
|
|
3521
|
+
* work. Examples from q-paper-neutron-scattering:
|
|
3522
|
+
* "did NOT generate exact Lee 2026 target states"
|
|
3523
|
+
* "did NOT touch Q-Work"
|
|
3524
|
+
* "did NOT change evidence labels"
|
|
3525
|
+
*
|
|
3526
|
+
* Scope locks are what make adversarial reviewer subagents tractable:
|
|
3527
|
+
* the reviewer doesn't have to guess what to check against, the actor
|
|
3528
|
+
* told them upfront.
|
|
3529
|
+
*/
|
|
3530
|
+
scopeLocks: z.array(z.string()).default([]),
|
|
3531
|
+
/**
|
|
3532
|
+
* Evidence tier this Episode's work was operating at.
|
|
3533
|
+
* Reviewer routing depends on this — engineering-proxy work can be
|
|
3534
|
+
* self-reviewed; external-facing work requires an adversarial reviewer
|
|
3535
|
+
* subagent; human-reviewed work requires a `request_review_gate` action.
|
|
3536
|
+
*/
|
|
3537
|
+
evidenceTier: EvidenceTierSchema,
|
|
3538
|
+
/** Token spend during this wake (input + output, all model calls). */
|
|
3539
|
+
tokensSpent: z.number().int().min(0).default(0),
|
|
3540
|
+
/** Cost in USD during this wake. */
|
|
3541
|
+
costUsd: z.number().min(0).default(0),
|
|
3542
|
+
/**
|
|
3543
|
+
* Optional pointer to a reviewer Episode that audited this one.
|
|
3544
|
+
* Set after an adversarial reviewer subagent has completed its pass.
|
|
3545
|
+
*/
|
|
3546
|
+
reviewedByEpisodeId: z.string().optional()
|
|
3547
|
+
});
|
|
3548
|
+
z.object({
|
|
3549
|
+
/**
|
|
3550
|
+
* - approved: claims hold up; tierGranted may certify tier advancement
|
|
3551
|
+
* - needs-changes: salvageable, but issues must be addressed first
|
|
3552
|
+
* - rejected: claims refuted or unsupported
|
|
3553
|
+
*/
|
|
3554
|
+
verdict: z.enum([
|
|
3555
|
+
"approved",
|
|
3556
|
+
"needs-changes",
|
|
3557
|
+
"rejected"
|
|
3558
|
+
]),
|
|
3559
|
+
/** Specific, checkable problems found (empty when approved clean). */
|
|
3560
|
+
issues: z.array(z.string()).default([]),
|
|
3561
|
+
/** Highest evidence tier the reviewer certifies for this work. */
|
|
3562
|
+
tierGranted: EvidenceTierSchema.optional(),
|
|
3563
|
+
/** One-paragraph justification of the verdict. */
|
|
3564
|
+
summary: z.string().min(1)
|
|
3565
|
+
});
|
|
3159
3566
|
String.raw`
|
|
3160
3567
|
const { parentPort } = require('node:worker_threads');
|
|
3161
3568
|
const vm = require('node:vm');
|
|
@@ -3954,6 +4361,7 @@ function mapMimeTypeToArtifactType(mimeType) {
|
|
|
3954
4361
|
case ClaudeArtifactMimeTypes.CODE: return "code";
|
|
3955
4362
|
case ClaudeArtifactMimeTypes.MARKDOWN: return "code";
|
|
3956
4363
|
case ClaudeArtifactMimeTypes.LATTICE: return "lattice";
|
|
4364
|
+
case ClaudeArtifactMimeTypes.BLOG_DRAFT: return "blog-draft";
|
|
3957
4365
|
default:
|
|
3958
4366
|
if (mimeType.includes("javascript") || mimeType.includes("jsx")) return "react";
|
|
3959
4367
|
if (mimeType.includes("html")) return "html";
|
|
@@ -7323,6 +7731,20 @@ var AIImageService = class {
|
|
|
7323
7731
|
}
|
|
7324
7732
|
};
|
|
7325
7733
|
/**
|
|
7734
|
+
* AWS Lambda hard limit for synchronous (RequestResponse) invocation payloads.
|
|
7735
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html
|
|
7736
|
+
*/
|
|
7737
|
+
const LAMBDA_SYNC_PAYLOAD_LIMIT_BYTES = 6291456;
|
|
7738
|
+
/**
|
|
7739
|
+
* Max raw image size we allow into the synchronous ImageProcessor invocation.
|
|
7740
|
+
* base64 inflates bytes by ~4/3, and the JSON envelope adds a small constant,
|
|
7741
|
+
* so a raw image > ~4.5 MB produces a payload over the 6 MB Lambda limit.
|
|
7742
|
+
* We guard at 4.4 MiB to leave margin for the JSON wrapper and key names.
|
|
7743
|
+
* Use binary MiB (matches how currentSizeMB is computed) so the constant,
|
|
7744
|
+
* the user-facing message, and the PR description all agree on "4.4MB".
|
|
7745
|
+
*/
|
|
7746
|
+
const MAX_RAW_IMAGE_BYTES = 4.4 * 1024 * 1024;
|
|
7747
|
+
/**
|
|
7326
7748
|
* Invokes the image processor Lambda to convert and resize images
|
|
7327
7749
|
* This is a serverless alternative to using sharp directly
|
|
7328
7750
|
*
|
|
@@ -7341,6 +7763,11 @@ async function invokeImageProcessor(imageBuffer, lambdaFunctionName, maxSizeMB =
|
|
|
7341
7763
|
}
|
|
7342
7764
|
console.log(`[ImageProcessorUtils] Processing needed - isPng: ${isPng}, needsResize: ${currentSizeMB > maxSizeMB}`);
|
|
7343
7765
|
if (!lambdaFunctionName) throw new Error("ImageProcessor Lambda function name is required. Please pass the Lambda function name as an argument.");
|
|
7766
|
+
if (imageBuffer.length > MAX_RAW_IMAGE_BYTES) {
|
|
7767
|
+
const projectedPayloadMB = (imageBuffer.length * 4 / 3 / (1024 * 1024)).toFixed(2);
|
|
7768
|
+
const maxRawMB = (MAX_RAW_IMAGE_BYTES / (1024 * 1024)).toFixed(1);
|
|
7769
|
+
throw new Error(`Image too large (${currentSizeMB.toFixed(2)}MB). Images sent for editing must be under ${maxRawMB}MB (encoding would produce a ~${projectedPayloadMB}MB request, exceeding the ${(LAMBDA_SYNC_PAYLOAD_LIMIT_BYTES / (1024 * 1024)).toFixed(0)}MB limit). Please resize the image and try again.`);
|
|
7770
|
+
}
|
|
7344
7771
|
const lambdaClient = new LambdaClient({});
|
|
7345
7772
|
const request = {
|
|
7346
7773
|
imageBuffer: imageBuffer.toString("base64"),
|
|
@@ -8559,6 +8986,24 @@ function findAutomaticFallback(originalModel, availableModels, apiKeyTable, logg
|
|
|
8559
8986
|
"claude-opus-4-6"
|
|
8560
8987
|
],
|
|
8561
8988
|
"gemini-1.5-flash": ["claude-haiku-4-5-20251001", "gpt-4o-mini"],
|
|
8989
|
+
"claude-fable-5": [
|
|
8990
|
+
"claude-opus-4-8",
|
|
8991
|
+
"claude-opus-4-7",
|
|
8992
|
+
"claude-opus-4-6",
|
|
8993
|
+
"claude-sonnet-4-6",
|
|
8994
|
+
"gpt-5"
|
|
8995
|
+
],
|
|
8996
|
+
"claude-opus-4-8": [
|
|
8997
|
+
"claude-opus-4-7",
|
|
8998
|
+
"claude-opus-4-6",
|
|
8999
|
+
"claude-sonnet-4-6",
|
|
9000
|
+
"gpt-5"
|
|
9001
|
+
],
|
|
9002
|
+
"claude-opus-4-7": [
|
|
9003
|
+
"claude-opus-4-6",
|
|
9004
|
+
"claude-sonnet-4-6",
|
|
9005
|
+
"gpt-5"
|
|
9006
|
+
],
|
|
8562
9007
|
"claude-opus-4-5-20251101": [
|
|
8563
9008
|
"claude-sonnet-4-6",
|
|
8564
9009
|
"claude-sonnet-4-5-20250929",
|
|
@@ -11881,11 +12326,11 @@ function tryDecodeSample(problem, opVarMap, stateIdx) {
|
|
|
11881
12326
|
*/
|
|
11882
12327
|
const MAX_QUBITS = 16;
|
|
11883
12328
|
/** QAOA layers (p). p=1 is standard for NISQ-era problems. */
|
|
11884
|
-
const LAYERS = 1;
|
|
12329
|
+
const LAYERS$1 = 1;
|
|
11885
12330
|
/** Grid steps per axis during parameter search (gammaSteps × betaSteps evaluations). */
|
|
11886
|
-
const GRID_STEPS = 8;
|
|
12331
|
+
const GRID_STEPS$1 = 8;
|
|
11887
12332
|
/** Bitstring samples drawn from the final optimised circuit. */
|
|
11888
|
-
const FINAL_SHOTS = 512;
|
|
12333
|
+
const FINAL_SHOTS$1 = 512;
|
|
11889
12334
|
/** All available solvers */
|
|
11890
12335
|
const allSolvers = [
|
|
11891
12336
|
greedySolver,
|
|
@@ -11924,7 +12369,7 @@ const allSolvers = [
|
|
|
11924
12369
|
schedule: greedyResult.schedule,
|
|
11925
12370
|
elapsedMs: elapsed(),
|
|
11926
12371
|
metadata: {
|
|
11927
|
-
layers: LAYERS,
|
|
12372
|
+
layers: LAYERS$1,
|
|
11928
12373
|
shots: 0,
|
|
11929
12374
|
gamma: 0,
|
|
11930
12375
|
beta: 0,
|
|
@@ -11937,14 +12382,14 @@ const allSolvers = [
|
|
|
11937
12382
|
const backend = new LocalSimBackend();
|
|
11938
12383
|
progress(20);
|
|
11939
12384
|
const { gamma, beta } = await gridSearchOptimize(numQubits, ising, backend, {
|
|
11940
|
-
gammaSteps: GRID_STEPS,
|
|
11941
|
-
betaSteps: GRID_STEPS,
|
|
11942
|
-
p: LAYERS
|
|
12385
|
+
gammaSteps: GRID_STEPS$1,
|
|
12386
|
+
betaSteps: GRID_STEPS$1,
|
|
12387
|
+
p: LAYERS$1
|
|
11943
12388
|
});
|
|
11944
12389
|
progress(75);
|
|
11945
|
-
const gates = buildQAOACircuit(numQubits, ising, [gamma], [beta], LAYERS);
|
|
12390
|
+
const gates = buildQAOACircuit(numQubits, ising, [gamma], [beta], LAYERS$1);
|
|
11946
12391
|
const { probabilities } = await backend.run(numQubits, gates);
|
|
11947
|
-
const samples = sampleFromProbabilities(probabilities, FINAL_SHOTS);
|
|
12392
|
+
const samples = sampleFromProbabilities(probabilities, FINAL_SHOTS$1);
|
|
11948
12393
|
progress(90);
|
|
11949
12394
|
const candidates = [decodeFromArgmax(problem, encoded.variables, probabilities, greedyResult.schedule), decodeSamples(problem, encoded.variables, samples)].filter((d) => d !== null);
|
|
11950
12395
|
const bestQuantum = candidates.length > 0 ? candidates.reduce((a, b) => b.makespan < a.makespan ? b : a) : null;
|
|
@@ -11958,8 +12403,8 @@ const allSolvers = [
|
|
|
11958
12403
|
schedule: winner.schedule,
|
|
11959
12404
|
elapsedMs: elapsed(),
|
|
11960
12405
|
metadata: {
|
|
11961
|
-
layers: LAYERS,
|
|
11962
|
-
shots: FINAL_SHOTS,
|
|
12406
|
+
layers: LAYERS$1,
|
|
12407
|
+
shots: FINAL_SHOTS$1,
|
|
11963
12408
|
gamma,
|
|
11964
12409
|
beta,
|
|
11965
12410
|
rawMakespan: bestQuantum?.makespan ?? greedyResult.makespan,
|
|
@@ -12020,12 +12465,12 @@ RESPOND WITH ONLY A JSON OBJECT matching this schema:
|
|
|
12020
12465
|
|
|
12021
12466
|
No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
12022
12467
|
[
|
|
12023
|
-
"
|
|
12024
|
-
"
|
|
12025
|
-
"
|
|
12026
|
-
"
|
|
12027
|
-
"
|
|
12028
|
-
"
|
|
12468
|
+
" 0 3 6 9 12",
|
|
12469
|
+
" ├───┼───┼───┼───┤",
|
|
12470
|
+
"M1 ████▓▓▓▓░░░",
|
|
12471
|
+
"M2 ░░░░████ ▓▓▓▓",
|
|
12472
|
+
"M3 ▓▓▓░░░░ ████",
|
|
12473
|
+
" └──── time ─────▶"
|
|
12029
12474
|
].join("\n"), [
|
|
12030
12475
|
"J1: [M-A 3t]->[M-B 2t]->[M-C 4t]",
|
|
12031
12476
|
"J2: [M-B 2t]->[M-C 3t]->[M-A 2t]",
|
|
@@ -12057,15 +12502,16 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
12057
12502
|
" J4 === (arrived t=5)",
|
|
12058
12503
|
" Must reschedule as jobs arrive!"
|
|
12059
12504
|
].join("\n"), [
|
|
12060
|
-
" A",
|
|
12061
|
-
"
|
|
12062
|
-
" 5
|
|
12063
|
-
"
|
|
12064
|
-
"
|
|
12065
|
-
"
|
|
12066
|
-
" 7
|
|
12067
|
-
"
|
|
12068
|
-
"
|
|
12505
|
+
" ●A",
|
|
12506
|
+
" ╱ ╲",
|
|
12507
|
+
" 8╱ ╲5",
|
|
12508
|
+
" ╱ ╲",
|
|
12509
|
+
" D● ●B",
|
|
12510
|
+
" ╲ ╱",
|
|
12511
|
+
" 7╲ ╱2",
|
|
12512
|
+
" ╲ ╱",
|
|
12513
|
+
" ●C",
|
|
12514
|
+
" tour A→B→C→D→A = 22"
|
|
12069
12515
|
].join("\n"), [
|
|
12070
12516
|
"Start -> A -> B -> C -> D -> Start",
|
|
12071
12517
|
"",
|
|
@@ -12085,13 +12531,12 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
12085
12531
|
"| | |",
|
|
12086
12532
|
"B --6-- C --2-- D"
|
|
12087
12533
|
].join("\n"), [
|
|
12088
|
-
"
|
|
12089
|
-
"
|
|
12090
|
-
"
|
|
12091
|
-
"
|
|
12092
|
-
"
|
|
12093
|
-
"
|
|
12094
|
-
"└──────────────────┘"
|
|
12534
|
+
" bin 1 bin 2",
|
|
12535
|
+
" ┌────────┐ ┌────────┐",
|
|
12536
|
+
" │███▓▓░░░│ │█████ │",
|
|
12537
|
+
" │███▓▓░░▒│ │▒▒▒ │",
|
|
12538
|
+
" └────────┘ └────────┘",
|
|
12539
|
+
" 94% full 61% full"
|
|
12095
12540
|
].join("\n"), [
|
|
12096
12541
|
"Items: [3] [5] [2] [4] [6]",
|
|
12097
12542
|
"",
|
|
@@ -12110,12 +12555,13 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
12110
12555
|
" waste",
|
|
12111
12556
|
"Objective: Minimize total waste"
|
|
12112
12557
|
].join("\n"), [
|
|
12113
|
-
"
|
|
12114
|
-
" [A]
|
|
12115
|
-
" [B]
|
|
12116
|
-
"
|
|
12117
|
-
" [
|
|
12118
|
-
"
|
|
12558
|
+
" agents tasks",
|
|
12559
|
+
" [A]─────────(1)",
|
|
12560
|
+
" [B]────┐",
|
|
12561
|
+
" [C]────┼────(2)",
|
|
12562
|
+
" [D]──┐ └────(3)",
|
|
12563
|
+
" └──────(4)",
|
|
12564
|
+
" min Σ cost ▼"
|
|
12119
12565
|
].join("\n"), [
|
|
12120
12566
|
"Workers Jobs",
|
|
12121
12567
|
" Alice ---> Design cost: 3",
|
|
@@ -12133,12 +12579,12 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
12133
12579
|
"Agent C (cap 12): [T6:6][T7:4]",
|
|
12134
12580
|
"Maximize quality within capacity"
|
|
12135
12581
|
].join("\n"), [
|
|
12136
|
-
"
|
|
12137
|
-
"
|
|
12138
|
-
" f
|
|
12139
|
-
"
|
|
12140
|
-
"
|
|
12141
|
-
"
|
|
12582
|
+
" a───b ╌╌┆╌╌ d",
|
|
12583
|
+
" │ ╲ │ ┆ │",
|
|
12584
|
+
" c───e ╌╌┆╌╌ f",
|
|
12585
|
+
" ┆",
|
|
12586
|
+
" group A ┆ group B",
|
|
12587
|
+
" maximize the cut ▲"
|
|
12142
12588
|
].join("\n"), [
|
|
12143
12589
|
"Group 0 | Group 1",
|
|
12144
12590
|
" a--b | d--e",
|
|
@@ -12157,12 +12603,43 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
12157
12603
|
" sparse",
|
|
12158
12604
|
"Find the natural communities"
|
|
12159
12605
|
].join("\n"), [
|
|
12160
|
-
"
|
|
12161
|
-
"
|
|
12162
|
-
"
|
|
12163
|
-
"
|
|
12164
|
-
"
|
|
12165
|
-
"
|
|
12606
|
+
" ┌──┬──╥──┬──┐",
|
|
12607
|
+
" │▒▒│▒▒║░░│░░│",
|
|
12608
|
+
" ├──┼──╫──┼──┤",
|
|
12609
|
+
" │▒▒│▒▒║░░│░░│",
|
|
12610
|
+
" └──┴──╨──┴──┘",
|
|
12611
|
+
" rank A ║ rank B",
|
|
12612
|
+
" minimize the seam ▲"
|
|
12613
|
+
].join("\n"), [
|
|
12614
|
+
" o─o─o ║ o─o─o",
|
|
12615
|
+
" │ │ │ ║ │ │ │",
|
|
12616
|
+
" o─o─o ║ o─o─o",
|
|
12617
|
+
" n/2 ║ n/2",
|
|
12618
|
+
" cut = xᵀLx ▲ minimize"
|
|
12619
|
+
].join("\n"), [
|
|
12620
|
+
" [ A A B B C C ]",
|
|
12621
|
+
" [ A A B B C C ]",
|
|
12622
|
+
" [ A A B B C C ]",
|
|
12623
|
+
" k=3 · each n/3",
|
|
12624
|
+
" seams pay, balance holds"
|
|
12625
|
+
].join("\n"), [
|
|
12626
|
+
" mesh ──▶ ranks",
|
|
12627
|
+
" ▒▒▒░░░ halo ↕ exchanged",
|
|
12628
|
+
" ▒▒▒░░░ every timestep",
|
|
12629
|
+
" cut faces = network tax"
|
|
12630
|
+
].join("\n"), [
|
|
12631
|
+
" ┌────┬────┐",
|
|
12632
|
+
" │blk1│blk2│←wires that",
|
|
12633
|
+
" ├────┼────┤ cross cost",
|
|
12634
|
+
" │blk3│blk4│ timing",
|
|
12635
|
+
" └────┴────┘"
|
|
12636
|
+
].join("\n"), [
|
|
12637
|
+
" ▣ ▢ ▣ ▢ ▣ ▢",
|
|
12638
|
+
" ▢ ▣ ▢ ▢ ▣ ▢",
|
|
12639
|
+
" ▣ ▢ ▢ ▢ ▢ ▣",
|
|
12640
|
+
" ─────────────",
|
|
12641
|
+
" ▣ picked 7/18",
|
|
12642
|
+
" value 847 ▲ max"
|
|
12166
12643
|
].join("\n"), [
|
|
12167
12644
|
"Return ^",
|
|
12168
12645
|
" | * efficient frontier",
|
|
@@ -12181,13 +12658,13 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
12181
12658
|
"S4={5,6,7} S5={1,6}",
|
|
12182
12659
|
"Solution: S1 + S3 + S4 (covers all)"
|
|
12183
12660
|
].join("\n"), [
|
|
12184
|
-
" P
|
|
12185
|
-
"
|
|
12186
|
-
"
|
|
12187
|
-
"
|
|
12188
|
-
"
|
|
12189
|
-
"
|
|
12190
|
-
"
|
|
12661
|
+
" P│ demand╲ ╱supply",
|
|
12662
|
+
" │ ╲ ╱",
|
|
12663
|
+
" │ ⊗ p*",
|
|
12664
|
+
" │ ╱ ╲",
|
|
12665
|
+
" │ ╱ ╲",
|
|
12666
|
+
" └─────────┴────── Q",
|
|
12667
|
+
" q*"
|
|
12191
12668
|
].join("\n"), [
|
|
12192
12669
|
"Buyers: B1=$50 B2=$40 B3=$30",
|
|
12193
12670
|
"Sellers: S1=$20 S2=$35 S3=$45",
|
|
@@ -12206,13 +12683,13 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
12206
12683
|
" $20 40 $800",
|
|
12207
12684
|
" $25 15 $375"
|
|
12208
12685
|
].join("\n"), [
|
|
12209
|
-
" f(x)",
|
|
12210
|
-
"
|
|
12211
|
-
"
|
|
12212
|
-
"
|
|
12213
|
-
"
|
|
12214
|
-
"
|
|
12215
|
-
"
|
|
12686
|
+
" f(x)│",
|
|
12687
|
+
" │╲ ╱╲ ╱",
|
|
12688
|
+
" │ ╲ ╱ ╲ ╱",
|
|
12689
|
+
" │ ╲ ╱ ╲ ╱",
|
|
12690
|
+
" │ ╳ ╲╱",
|
|
12691
|
+
" │ local ▼ min",
|
|
12692
|
+
" └──────────────── x"
|
|
12216
12693
|
].join("\n"), [
|
|
12217
12694
|
"Param A: [0.1, 0.2, 0.3, 0.4]",
|
|
12218
12695
|
"Param B: [10, 20, 30, 40, 50]",
|
|
@@ -12232,8 +12709,387 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
12232
12709
|
" \\/ <- fit here",
|
|
12233
12710
|
"Minimize: sum(error^2)"
|
|
12234
12711
|
].join("\n");
|
|
12712
|
+
/** Build a jittered grid mesh: cells at (col,row), edges between face-sharing neighbors. */
|
|
12713
|
+
function gridMesh(cols, rows, cellName, weightAt, jitter) {
|
|
12714
|
+
const nodes = [];
|
|
12715
|
+
const padX = 100 / (cols + 1);
|
|
12716
|
+
const padY = 100 / (rows + 1);
|
|
12717
|
+
for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) {
|
|
12718
|
+
const i = r * cols + c;
|
|
12719
|
+
const { dx, dy } = jitter(i);
|
|
12720
|
+
nodes.push({
|
|
12721
|
+
id: i,
|
|
12722
|
+
name: cellName(i),
|
|
12723
|
+
x: Math.round(padX * (c + 1) + dx),
|
|
12724
|
+
y: Math.round(padY * (r + 1) + dy)
|
|
12725
|
+
});
|
|
12726
|
+
}
|
|
12727
|
+
const edges = [];
|
|
12728
|
+
for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) {
|
|
12729
|
+
const i = r * cols + c;
|
|
12730
|
+
if (c + 1 < cols) edges.push({
|
|
12731
|
+
a: i,
|
|
12732
|
+
b: i + 1,
|
|
12733
|
+
w: weightAt(i, i + 1)
|
|
12734
|
+
});
|
|
12735
|
+
if (r + 1 < rows) edges.push({
|
|
12736
|
+
a: i,
|
|
12737
|
+
b: i + cols,
|
|
12738
|
+
w: weightAt(i, i + cols)
|
|
12739
|
+
});
|
|
12740
|
+
}
|
|
12741
|
+
return {
|
|
12742
|
+
nodes,
|
|
12743
|
+
edges
|
|
12744
|
+
};
|
|
12745
|
+
}
|
|
12746
|
+
const wobble = (i) => i * 7919 % 11 / 10 - .5;
|
|
12747
|
+
const plate = gridMesh(4, 3, (i) => `cell ${i}`, (a, b) => 1 + (a * 31 + b * 17) % 4, (i) => ({
|
|
12748
|
+
dx: wobble(i) * 6,
|
|
12749
|
+
dy: wobble(i + 3) * 6
|
|
12750
|
+
}));
|
|
12751
|
+
const reservoir = gridMesh(6, 3, (i) => `block ${i}`, (a, b) => 1 + (a * 13 + b * 29) % 5, (i) => ({
|
|
12752
|
+
dx: wobble(i + 1) * 8,
|
|
12753
|
+
dy: wobble(i + 5) * 8
|
|
12754
|
+
}));
|
|
12755
|
+
const blade = gridMesh(6, 4, (i) => `elem ${i}`, (a, b) => 1 + (a * 23 + b * 19) % 4, (i) => ({
|
|
12756
|
+
dx: wobble(i + 2) * 7,
|
|
12757
|
+
dy: wobble(i + 7) * 7
|
|
12758
|
+
}));
|
|
12759
|
+
plate.nodes, plate.edges, reservoir.nodes, reservoir.edges, blade.nodes, blade.edges;
|
|
12760
|
+
var QuboBuilder = class {
|
|
12761
|
+
size;
|
|
12762
|
+
q = /* @__PURE__ */ new Map();
|
|
12763
|
+
constructor(size) {
|
|
12764
|
+
this.size = size;
|
|
12765
|
+
}
|
|
12766
|
+
add(i, j, v) {
|
|
12767
|
+
const [a, b] = i <= j ? [i, j] : [j, i];
|
|
12768
|
+
const key = `${a},${b}`;
|
|
12769
|
+
this.q.set(key, (this.q.get(key) ?? 0) + v);
|
|
12770
|
+
}
|
|
12771
|
+
entries() {
|
|
12772
|
+
return [...this.q.entries()].map(([key, v]) => {
|
|
12773
|
+
const [i, j] = key.split(",").map(Number);
|
|
12774
|
+
return [
|
|
12775
|
+
i,
|
|
12776
|
+
j,
|
|
12777
|
+
v
|
|
12778
|
+
];
|
|
12779
|
+
});
|
|
12780
|
+
}
|
|
12781
|
+
};
|
|
12782
|
+
/** Add a one-hot penalty P·(Σx − 1)² over the given variable indices. */
|
|
12783
|
+
function oneHot(b, vars, P) {
|
|
12784
|
+
for (const i of vars) b.add(i, i, -P);
|
|
12785
|
+
for (let a = 0; a < vars.length; a++) for (let c = a + 1; c < vars.length; c++) b.add(vars[a], vars[c], 2 * P);
|
|
12786
|
+
}
|
|
12787
|
+
function schedulingToy() {
|
|
12788
|
+
const b = new QuboBuilder(12);
|
|
12789
|
+
const v = (op, t) => op * 4 + t;
|
|
12790
|
+
for (let op = 0; op < 3; op++) {
|
|
12791
|
+
oneHot(b, [
|
|
12792
|
+
0,
|
|
12793
|
+
1,
|
|
12794
|
+
2,
|
|
12795
|
+
3
|
|
12796
|
+
].map((t) => v(op, t)), 4);
|
|
12797
|
+
for (let t = 0; t < 4; t++) b.add(v(op, t), v(op, t), t);
|
|
12798
|
+
}
|
|
12799
|
+
for (let t = 0; t < 4; t++) b.add(v(0, t), v(1, t), 6);
|
|
12800
|
+
return {
|
|
12801
|
+
familyId: "scheduling",
|
|
12802
|
+
size: 12,
|
|
12803
|
+
entries: b.entries(),
|
|
12804
|
+
variableGloss: "x[i,t] = 1 ⇔ operation i starts at time t",
|
|
12805
|
+
constraintGloss: "one start per op · no two ops share a machine-slot"
|
|
12806
|
+
};
|
|
12807
|
+
}
|
|
12808
|
+
function routingToy() {
|
|
12809
|
+
const b = new QuboBuilder(16);
|
|
12810
|
+
const v = (city, pos) => city * 4 + pos;
|
|
12811
|
+
const D = [
|
|
12812
|
+
[
|
|
12813
|
+
0,
|
|
12814
|
+
2,
|
|
12815
|
+
3,
|
|
12816
|
+
2
|
|
12817
|
+
],
|
|
12818
|
+
[
|
|
12819
|
+
2,
|
|
12820
|
+
0,
|
|
12821
|
+
2,
|
|
12822
|
+
3
|
|
12823
|
+
],
|
|
12824
|
+
[
|
|
12825
|
+
3,
|
|
12826
|
+
2,
|
|
12827
|
+
0,
|
|
12828
|
+
2
|
|
12829
|
+
],
|
|
12830
|
+
[
|
|
12831
|
+
2,
|
|
12832
|
+
3,
|
|
12833
|
+
2,
|
|
12834
|
+
0
|
|
12835
|
+
]
|
|
12836
|
+
];
|
|
12837
|
+
for (let c = 0; c < 4; c++) oneHot(b, [
|
|
12838
|
+
0,
|
|
12839
|
+
1,
|
|
12840
|
+
2,
|
|
12841
|
+
3
|
|
12842
|
+
].map((p) => v(c, p)), 5);
|
|
12843
|
+
for (let p = 0; p < 4; p++) oneHot(b, [
|
|
12844
|
+
0,
|
|
12845
|
+
1,
|
|
12846
|
+
2,
|
|
12847
|
+
3
|
|
12848
|
+
].map((c) => v(c, p)), 5);
|
|
12849
|
+
for (let p = 0; p < 4; p++) {
|
|
12850
|
+
const next = (p + 1) % 4;
|
|
12851
|
+
for (let c1 = 0; c1 < 4; c1++) for (let c2 = 0; c2 < 4; c2++) if (c1 !== c2) b.add(v(c1, p), v(c2, next), D[c1][c2]);
|
|
12852
|
+
}
|
|
12853
|
+
return {
|
|
12854
|
+
familyId: "routing",
|
|
12855
|
+
size: 16,
|
|
12856
|
+
entries: b.entries(),
|
|
12857
|
+
variableGloss: "x[c,p] = 1 ⇔ city c is visited at tour position p",
|
|
12858
|
+
constraintGloss: "each city once · each position once · pay the leg distance"
|
|
12859
|
+
};
|
|
12860
|
+
}
|
|
12861
|
+
function packingToy() {
|
|
12862
|
+
const b = new QuboBuilder(12);
|
|
12863
|
+
const v = (item, bin) => item * 3 + bin;
|
|
12864
|
+
const sizes = [
|
|
12865
|
+
3,
|
|
12866
|
+
2,
|
|
12867
|
+
2,
|
|
12868
|
+
1
|
|
12869
|
+
];
|
|
12870
|
+
for (let i = 0; i < 4; i++) oneHot(b, [
|
|
12871
|
+
0,
|
|
12872
|
+
1,
|
|
12873
|
+
2
|
|
12874
|
+
].map((bin) => v(i, bin)), 5);
|
|
12875
|
+
for (let bin = 0; bin < 3; bin++) {
|
|
12876
|
+
for (let i = 0; i < 4; i++) for (let j = i + 1; j < 4; j++) if (sizes[i] + sizes[j] > 4) b.add(v(i, bin), v(j, bin), 3);
|
|
12877
|
+
b.add(v(0, bin), v(0, bin), bin);
|
|
12878
|
+
}
|
|
12879
|
+
return {
|
|
12880
|
+
familyId: "packing",
|
|
12881
|
+
size: 12,
|
|
12882
|
+
entries: b.entries(),
|
|
12883
|
+
variableGloss: "x[i,b] = 1 ⇔ item i rides in bin b",
|
|
12884
|
+
constraintGloss: "each item in one bin · oversized pairs repel each other"
|
|
12885
|
+
};
|
|
12886
|
+
}
|
|
12887
|
+
function assignmentToy() {
|
|
12888
|
+
const b = new QuboBuilder(16);
|
|
12889
|
+
const v = (agent, task) => agent * 4 + task;
|
|
12890
|
+
const C = [
|
|
12891
|
+
[
|
|
12892
|
+
1,
|
|
12893
|
+
4,
|
|
12894
|
+
3,
|
|
12895
|
+
2
|
|
12896
|
+
],
|
|
12897
|
+
[
|
|
12898
|
+
3,
|
|
12899
|
+
1,
|
|
12900
|
+
4,
|
|
12901
|
+
2
|
|
12902
|
+
],
|
|
12903
|
+
[
|
|
12904
|
+
2,
|
|
12905
|
+
3,
|
|
12906
|
+
1,
|
|
12907
|
+
4
|
|
12908
|
+
],
|
|
12909
|
+
[
|
|
12910
|
+
4,
|
|
12911
|
+
2,
|
|
12912
|
+
3,
|
|
12913
|
+
1
|
|
12914
|
+
]
|
|
12915
|
+
];
|
|
12916
|
+
for (let a = 0; a < 4; a++) oneHot(b, [
|
|
12917
|
+
0,
|
|
12918
|
+
1,
|
|
12919
|
+
2,
|
|
12920
|
+
3
|
|
12921
|
+
].map((t) => v(a, t)), 5);
|
|
12922
|
+
for (let t = 0; t < 4; t++) oneHot(b, [
|
|
12923
|
+
0,
|
|
12924
|
+
1,
|
|
12925
|
+
2,
|
|
12926
|
+
3
|
|
12927
|
+
].map((a) => v(a, t)), 5);
|
|
12928
|
+
for (let a = 0; a < 4; a++) for (let t = 0; t < 4; t++) b.add(v(a, t), v(a, t), C[a][t]);
|
|
12929
|
+
return {
|
|
12930
|
+
familyId: "assignment",
|
|
12931
|
+
size: 16,
|
|
12932
|
+
entries: b.entries(),
|
|
12933
|
+
variableGloss: "x[a,t] = 1 ⇔ agent a takes task t",
|
|
12934
|
+
constraintGloss: "one task per agent · one agent per task · pay the mismatch"
|
|
12935
|
+
};
|
|
12936
|
+
}
|
|
12937
|
+
function networkToy() {
|
|
12938
|
+
const b = new QuboBuilder(10);
|
|
12939
|
+
for (const [i, j] of [
|
|
12940
|
+
[0, 1],
|
|
12941
|
+
[0, 4],
|
|
12942
|
+
[1, 2],
|
|
12943
|
+
[1, 4],
|
|
12944
|
+
[2, 5],
|
|
12945
|
+
[3, 4],
|
|
12946
|
+
[3, 7],
|
|
12947
|
+
[4, 5],
|
|
12948
|
+
[4, 8],
|
|
12949
|
+
[5, 6],
|
|
12950
|
+
[5, 9],
|
|
12951
|
+
[6, 9],
|
|
12952
|
+
[7, 8],
|
|
12953
|
+
[8, 9]
|
|
12954
|
+
]) {
|
|
12955
|
+
b.add(i, i, -1);
|
|
12956
|
+
b.add(j, j, -1);
|
|
12957
|
+
b.add(i, j, 2);
|
|
12958
|
+
}
|
|
12959
|
+
return {
|
|
12960
|
+
familyId: "network",
|
|
12961
|
+
size: 10,
|
|
12962
|
+
entries: b.entries(),
|
|
12963
|
+
variableGloss: "x[i] = 1 ⇔ node i goes to side B",
|
|
12964
|
+
constraintGloss: "no constraints — max cut IS the raw QUBO"
|
|
12965
|
+
};
|
|
12966
|
+
}
|
|
12967
|
+
function partitioningToy() {
|
|
12968
|
+
const b = new QuboBuilder(9);
|
|
12969
|
+
const edges = [];
|
|
12970
|
+
for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) {
|
|
12971
|
+
const i = r * 3 + c;
|
|
12972
|
+
if (c < 2) edges.push([i, i + 1]);
|
|
12973
|
+
if (r < 2) edges.push([i, i + 3]);
|
|
12974
|
+
}
|
|
12975
|
+
for (const [i, j] of edges) {
|
|
12976
|
+
b.add(i, i, 1);
|
|
12977
|
+
b.add(j, j, 1);
|
|
12978
|
+
b.add(i, j, -2);
|
|
12979
|
+
}
|
|
12980
|
+
const Pb = 1.5;
|
|
12981
|
+
const m = 4.5;
|
|
12982
|
+
for (let i = 0; i < 9; i++) {
|
|
12983
|
+
b.add(i, i, Pb * (1 - 2 * m));
|
|
12984
|
+
for (let j = i + 1; j < 9; j++) b.add(i, j, 2 * Pb);
|
|
12985
|
+
}
|
|
12986
|
+
return {
|
|
12987
|
+
familyId: "partitioning",
|
|
12988
|
+
size: 9,
|
|
12989
|
+
entries: b.entries(),
|
|
12990
|
+
variableGloss: "x[i] = 1 ⇔ mesh cell i goes to rank B",
|
|
12991
|
+
constraintGloss: "minimize the cut — literally xᵀLx, the graph Laplacian · balance is a spring on Σx"
|
|
12992
|
+
};
|
|
12993
|
+
}
|
|
12994
|
+
function selectionToy() {
|
|
12995
|
+
const b = new QuboBuilder(10);
|
|
12996
|
+
const value = [
|
|
12997
|
+
6,
|
|
12998
|
+
5,
|
|
12999
|
+
8,
|
|
13000
|
+
3,
|
|
13001
|
+
7,
|
|
13002
|
+
4,
|
|
13003
|
+
6,
|
|
13004
|
+
2,
|
|
13005
|
+
5,
|
|
13006
|
+
4
|
|
13007
|
+
];
|
|
13008
|
+
const cost = [
|
|
13009
|
+
3,
|
|
13010
|
+
2,
|
|
13011
|
+
4,
|
|
13012
|
+
1,
|
|
13013
|
+
3,
|
|
13014
|
+
2,
|
|
13015
|
+
3,
|
|
13016
|
+
1,
|
|
13017
|
+
2,
|
|
13018
|
+
2
|
|
13019
|
+
];
|
|
13020
|
+
const B = 10;
|
|
13021
|
+
const P = 1.2;
|
|
13022
|
+
for (let i = 0; i < 10; i++) {
|
|
13023
|
+
b.add(i, i, -value[i] + P * (cost[i] * cost[i] - 2 * B * cost[i]));
|
|
13024
|
+
for (let j = i + 1; j < 10; j++) b.add(i, j, P * 2 * cost[i] * cost[j]);
|
|
13025
|
+
}
|
|
13026
|
+
return {
|
|
13027
|
+
familyId: "selection",
|
|
13028
|
+
size: 10,
|
|
13029
|
+
entries: b.entries(),
|
|
13030
|
+
variableGloss: "x[i] = 1 ⇔ item i makes the portfolio",
|
|
13031
|
+
constraintGloss: "budget enforced as a quadratic spring around B"
|
|
13032
|
+
};
|
|
13033
|
+
}
|
|
13034
|
+
function economicToy() {
|
|
13035
|
+
const b = new QuboBuilder(10);
|
|
13036
|
+
const price = [
|
|
13037
|
+
8,
|
|
13038
|
+
5,
|
|
13039
|
+
9,
|
|
13040
|
+
4,
|
|
13041
|
+
7,
|
|
13042
|
+
6,
|
|
13043
|
+
5,
|
|
13044
|
+
3,
|
|
13045
|
+
6,
|
|
13046
|
+
4
|
|
13047
|
+
];
|
|
13048
|
+
const conflicts = [
|
|
13049
|
+
[0, 1],
|
|
13050
|
+
[0, 2],
|
|
13051
|
+
[1, 3],
|
|
13052
|
+
[2, 4],
|
|
13053
|
+
[2, 5],
|
|
13054
|
+
[3, 5],
|
|
13055
|
+
[4, 6],
|
|
13056
|
+
[5, 7],
|
|
13057
|
+
[6, 8],
|
|
13058
|
+
[7, 9],
|
|
13059
|
+
[8, 9]
|
|
13060
|
+
];
|
|
13061
|
+
for (let i = 0; i < 10; i++) b.add(i, i, -price[i]);
|
|
13062
|
+
for (const [i, j] of conflicts) b.add(i, j, 12);
|
|
13063
|
+
return {
|
|
13064
|
+
familyId: "economic",
|
|
13065
|
+
size: 10,
|
|
13066
|
+
entries: b.entries(),
|
|
13067
|
+
variableGloss: "x[i] = 1 ⇔ bid i wins its bundle",
|
|
13068
|
+
constraintGloss: "bids sharing a good repel — one sale per asset"
|
|
13069
|
+
};
|
|
13070
|
+
}
|
|
13071
|
+
function continuousToy() {
|
|
13072
|
+
const b = new QuboBuilder(8);
|
|
13073
|
+
const target = 9;
|
|
13074
|
+
for (let p = 0; p < 2; p++) {
|
|
13075
|
+
const base = p * 4;
|
|
13076
|
+
for (let j = 0; j < 4; j++) {
|
|
13077
|
+
const wj = 2 ** j;
|
|
13078
|
+
b.add(base + j, base + j, wj * wj - 2 * target * wj);
|
|
13079
|
+
for (let k = j + 1; k < 4; k++) b.add(base + j, base + k, 2 * wj * 2 ** k);
|
|
13080
|
+
}
|
|
13081
|
+
}
|
|
13082
|
+
return {
|
|
13083
|
+
familyId: "continuous",
|
|
13084
|
+
size: 8,
|
|
13085
|
+
entries: b.entries(),
|
|
13086
|
+
variableGloss: "x = Σ 2ᵏ·bₖ — a dial spelled in bits",
|
|
13087
|
+
constraintGloss: "(x − x*)² expands into pairwise bit couplings"
|
|
13088
|
+
};
|
|
13089
|
+
}
|
|
13090
|
+
schedulingToy(), routingToy(), packingToy(), assignmentToy(), networkToy(), partitioningToy(), selectionToy(), economicToy(), continuousToy();
|
|
12235
13091
|
//#endregion
|
|
12236
|
-
//#region ../../b4m-core/services/dist/tools-
|
|
13092
|
+
//#region ../../b4m-core/services/dist/tools-4APomBDv.mjs
|
|
12237
13093
|
async function performDeepResearch(context, params, config = {}) {
|
|
12238
13094
|
const maxDepth = config.maxDepth || 7;
|
|
12239
13095
|
const duration = config.duration || 4.5;
|
|
@@ -12603,6 +13459,72 @@ async function getDynamicDataLakeAccess(context) {
|
|
|
12603
13459
|
dataLakeTagPrefixes: accessibleLakes.map((dl) => dl.fileTagPrefix)
|
|
12604
13460
|
};
|
|
12605
13461
|
}
|
|
13462
|
+
async function semanticDataLakeSearch(params, adapters) {
|
|
13463
|
+
const { userId, userGroups = [], query, tags = [], topK = 10, minScore = 0, embeddingModel, apiKeyTable, dataLakeTags, dataLakeTagPrefixes, maxFiles = 2e3, chunkLoadCap = 1e4, logger } = params;
|
|
13464
|
+
const empty = {
|
|
13465
|
+
results: [],
|
|
13466
|
+
totalChunksSearched: 0,
|
|
13467
|
+
filesInScope: 0,
|
|
13468
|
+
embeddingModel
|
|
13469
|
+
};
|
|
13470
|
+
if (!query.trim() || dataLakeTags.length === 0) return empty;
|
|
13471
|
+
const provider = getProviderFromModel(embeddingModel);
|
|
13472
|
+
const embeddingConfig = {};
|
|
13473
|
+
if (provider === "openai") {
|
|
13474
|
+
if (!apiKeyTable?.openai) throw new Error("OpenAI API key required for semantic search but not found.");
|
|
13475
|
+
embeddingConfig.openaiApiKey = apiKeyTable.openai;
|
|
13476
|
+
} else if (provider === "voyageai") {
|
|
13477
|
+
if (!apiKeyTable?.voyageai) throw new Error("VoyageAI API key required for semantic search but not found.");
|
|
13478
|
+
embeddingConfig.voyageApiKey = apiKeyTable.voyageai;
|
|
13479
|
+
}
|
|
13480
|
+
const queryEmbedding = await new EmbeddingFactory(embeddingConfig).createEmbeddingService(embeddingModel).generateEmbedding(query);
|
|
13481
|
+
const queryDim = queryEmbedding.length;
|
|
13482
|
+
const fileSearch = await adapters.db.fabfiles.search(userId, "", {
|
|
13483
|
+
tags,
|
|
13484
|
+
shared: false
|
|
13485
|
+
}, {
|
|
13486
|
+
page: 1,
|
|
13487
|
+
limit: maxFiles
|
|
13488
|
+
}, {
|
|
13489
|
+
by: "fileName",
|
|
13490
|
+
direction: "asc"
|
|
13491
|
+
}, {
|
|
13492
|
+
textSearch: false,
|
|
13493
|
+
includeShared: true,
|
|
13494
|
+
userGroups,
|
|
13495
|
+
dataLakeTags,
|
|
13496
|
+
dataLakeTagPrefixes,
|
|
13497
|
+
excludeContent: true
|
|
13498
|
+
});
|
|
13499
|
+
const fileIds = fileSearch.data.map((f) => f.id);
|
|
13500
|
+
if (fileIds.length === 0) return empty;
|
|
13501
|
+
const fileById = new Map(fileSearch.data.map((f) => [f.id, f]));
|
|
13502
|
+
const chunks = await adapters.db.fabfilechunks.findVectorsByFabFileIds(fileIds, chunkLoadCap);
|
|
13503
|
+
const scored = [];
|
|
13504
|
+
for (const chunk of chunks) {
|
|
13505
|
+
if (!chunk.vector || chunk.vector.length !== queryDim) continue;
|
|
13506
|
+
const score = computeCosineSimilarity(queryEmbedding, chunk.vector);
|
|
13507
|
+
if (score < minScore) continue;
|
|
13508
|
+
const file = fileById.get(chunk.fabFileId);
|
|
13509
|
+
if (!file) continue;
|
|
13510
|
+
scored.push({
|
|
13511
|
+
chunkId: chunk.id,
|
|
13512
|
+
fileId: chunk.fabFileId,
|
|
13513
|
+
fileName: file.fileName,
|
|
13514
|
+
fileTags: file.tags?.map((t) => t.name) ?? [],
|
|
13515
|
+
chunkText: chunk.text ?? "",
|
|
13516
|
+
score
|
|
13517
|
+
});
|
|
13518
|
+
}
|
|
13519
|
+
scored.sort((a, b) => b.score - a.score);
|
|
13520
|
+
logger?.debug?.(`[semanticDataLakeSearch] ${fileIds.length} files, ${chunks.length} chunks → ${scored.length} above min ${minScore}, top score ${scored[0]?.score?.toFixed(3) ?? "n/a"}`);
|
|
13521
|
+
return {
|
|
13522
|
+
results: scored.slice(0, topK),
|
|
13523
|
+
totalChunksSearched: chunks.length,
|
|
13524
|
+
filesInScope: fileIds.length,
|
|
13525
|
+
embeddingModel
|
|
13526
|
+
};
|
|
13527
|
+
}
|
|
12606
13528
|
const diceRoll = async (parameters) => {
|
|
12607
13529
|
if (!parameters?.sides || !parameters?.times) throw new Error("Tool dice roll: Missing required parameters");
|
|
12608
13530
|
return sum(times(parameters.times, () => random(1, parameters.sides))).toString();
|
|
@@ -14349,6 +15271,49 @@ function parseTransformationResult(llmResponse) {
|
|
|
14349
15271
|
throw new Error("Failed to parse transformation result from LLM");
|
|
14350
15272
|
}
|
|
14351
15273
|
}
|
|
15274
|
+
/**
|
|
15275
|
+
* Sanitize a title for safe, display-clean embedding in the <artifact title="…">
|
|
15276
|
+
* attribute. The pristine title lives in the JSON body (which is what the preview
|
|
15277
|
+
* card renders); this attribute is only used as a label/list value and for id
|
|
15278
|
+
* resolution. So we strip the parse-breaking characters rather than HTML-entity-
|
|
15279
|
+
* encode them — entity encoding renders as "&"/"<" gibberish wherever
|
|
15280
|
+
* `metadata.title` is shown verbatim (knowledge viewer list, etc.). See #8905 review.
|
|
15281
|
+
*
|
|
15282
|
+
* - newlines/tabs → space: the attribute regexes use `.*?`, which won't cross newlines.
|
|
15283
|
+
* - strip <,>: keep the tag/attribute matchers ([^>]) from breaking.
|
|
15284
|
+
* - straight quotes → typographic quotes: the value matcher is [^"'], so BOTH a "
|
|
15285
|
+
* and a ' (e.g. the apostrophe in "Can't") would terminate it early and truncate
|
|
15286
|
+
* the title. Curly quotes (’ ” “) aren't in that class, so they're parse-safe and
|
|
15287
|
+
* still read naturally.
|
|
15288
|
+
* `&` is left as-is: it doesn't break the regexes and React renders it correctly.
|
|
15289
|
+
*/
|
|
15290
|
+
function sanitizeArtifactTitle(title) {
|
|
15291
|
+
return title.replace(/[\r\n\t]+/g, " ").replace(/[<>]/g, "").replace(/'/g, "’").replace(/"/g, "”").replace(/\s+/g, " ").trim();
|
|
15292
|
+
}
|
|
15293
|
+
/**
|
|
15294
|
+
* Wrap a drafted blog result in an <artifact> tag so it is surfaced as a
|
|
15295
|
+
* first-class artifact (streamed into the reply AND persisted via the
|
|
15296
|
+
* sharedToolBuilder tool_result extractor). See #8904.
|
|
15297
|
+
*
|
|
15298
|
+
* Two embedding concerns are handled here:
|
|
15299
|
+
* - Title goes in a tag attribute → sanitized via sanitizeArtifactTitle (parse-safe,
|
|
15300
|
+
* display-clean; the real title is preserved untouched in the JSON body).
|
|
15301
|
+
* - Blog prose can legitimately contain the literal "</artifact>" sequence, which
|
|
15302
|
+
* would truncate the non-greedy artifact-body regex. We escape it as "<\/artifact>";
|
|
15303
|
+
* JSON.parse treats "\/" as "/" and restores the original losslessly on the client.
|
|
15304
|
+
*/
|
|
15305
|
+
function wrapDraftAsArtifact(result, identifier) {
|
|
15306
|
+
const artifactTitle = sanitizeArtifactTitle(result.title);
|
|
15307
|
+
const artifactBody = JSON.stringify(result, null, 2).replace(/<\/artifact>/gi, "<\\/artifact>");
|
|
15308
|
+
return `✨ Blog draft created successfully!
|
|
15309
|
+
|
|
15310
|
+
<artifact identifier="${identifier}" type="${ClaudeArtifactMimeTypes.BLOG_DRAFT}" title="${artifactTitle}">
|
|
15311
|
+
${artifactBody}
|
|
15312
|
+
</artifact>
|
|
15313
|
+
|
|
15314
|
+
📋 The preview card above is ready for you to review and edit before publishing.
|
|
15315
|
+
`;
|
|
15316
|
+
}
|
|
14352
15317
|
const blogDraftTool = {
|
|
14353
15318
|
name: "blog_draft",
|
|
14354
15319
|
implementation: (context) => ({
|
|
@@ -14390,14 +15355,7 @@ const blogDraftTool = {
|
|
|
14390
15355
|
contentLength: result.content.length,
|
|
14391
15356
|
tagsCount: result.suggestedTags.length
|
|
14392
15357
|
});
|
|
14393
|
-
return
|
|
14394
|
-
|
|
14395
|
-
\`\`\`json
|
|
14396
|
-
${JSON.stringify(result, null, 2)}
|
|
14397
|
-
\`\`\`
|
|
14398
|
-
|
|
14399
|
-
📋 The preview card will appear below for you to review and edit before publishing.
|
|
14400
|
-
`;
|
|
15358
|
+
return wrapDraftAsArtifact(result, `blog-draft-${randomUUID()}`);
|
|
14401
15359
|
} catch (error) {
|
|
14402
15360
|
logger.error("Blog draft creation failed:", error);
|
|
14403
15361
|
throw error;
|
|
@@ -15707,6 +16665,95 @@ const planetVisibilityTool = {
|
|
|
15707
16665
|
}
|
|
15708
16666
|
})
|
|
15709
16667
|
};
|
|
16668
|
+
const CHUNK_TEXT_CAP = 1200;
|
|
16669
|
+
/** Clean "[Category] 01 Some Name.md" → "Some Name" for display. */
|
|
16670
|
+
function prettyFileName(fn) {
|
|
16671
|
+
return fn.replace(/\.[a-z0-9]+$/i, "").replace(/^\[[^\]]*\]\s*/, "").replace(/^\d+[\s._-]*/, "").replace(/[-_]+/g, " ").trim();
|
|
16672
|
+
}
|
|
16673
|
+
/** Format semantic passages WITH their content so the model can answer without retrieving. */
|
|
16674
|
+
function formatSemanticResults(results) {
|
|
16675
|
+
const blocks = results.map((r, i) => {
|
|
16676
|
+
const text = r.chunkText.trim();
|
|
16677
|
+
const clipped = text.length > CHUNK_TEXT_CAP ? `${text.slice(0, CHUNK_TEXT_CAP)}…` : text;
|
|
16678
|
+
return `${i + 1}. **${prettyFileName(r.fileName)}** (relevance ${r.score.toFixed(2)})\n${clipped}`;
|
|
16679
|
+
});
|
|
16680
|
+
return `Found ${results.length} relevant passage(s) in the knowledge base — the content is included below, so answer directly and only call retrieve_knowledge_content if you need MORE detail from a specific file:\n\n` + blocks.join("\n\n---\n\n");
|
|
16681
|
+
}
|
|
16682
|
+
/**
|
|
16683
|
+
* Semantic-first KB search: embed the query and cosine-rank against the pre-computed chunk
|
|
16684
|
+
* vectors (tag-independent, ranks by meaning), returning the matching passage TEXT inline so
|
|
16685
|
+
* the model answers without a search→retrieve-N loop. Returns null to fall through to the
|
|
16686
|
+
* keyword path when embedding deps are unavailable or nothing matches.
|
|
16687
|
+
*/
|
|
16688
|
+
async function trySemanticKbSearch(context, query, tags, maxResults) {
|
|
16689
|
+
const chunkRepo = context.db.fabfilechunks;
|
|
16690
|
+
const adminSettings = context.db.adminSettings;
|
|
16691
|
+
const apiKeys = context.db.apiKeys;
|
|
16692
|
+
if (!context.db.fabfiles || !chunkRepo?.findVectorsByFabFileIds || !adminSettings || !apiKeys) return null;
|
|
16693
|
+
try {
|
|
16694
|
+
const modelRaw = await adminSettings.getSettingsValue("defaultEmbeddingModel");
|
|
16695
|
+
if (!modelRaw || !isSupportedEmbeddingModel(modelRaw)) return null;
|
|
16696
|
+
const embeddingModel = modelRaw;
|
|
16697
|
+
const apiKeyTable = await (0, apiKeyService_exports.getEffectiveLLMApiKeys)(context.userId, {
|
|
16698
|
+
db: {
|
|
16699
|
+
apiKeys,
|
|
16700
|
+
adminSettings
|
|
16701
|
+
},
|
|
16702
|
+
getSettingsByNames
|
|
16703
|
+
}, { logger: context.logger });
|
|
16704
|
+
const provider = getProviderFromModel(embeddingModel);
|
|
16705
|
+
if (provider === "openai" && !apiKeyTable?.openai) return null;
|
|
16706
|
+
if (provider === "voyageai" && !apiKeyTable?.voyageai) return null;
|
|
16707
|
+
const { dataLakeTags, dataLakeTagPrefixes } = await getDynamicDataLakeAccess(context);
|
|
16708
|
+
if (dataLakeTags.length === 0) return null;
|
|
16709
|
+
const search = await semanticDataLakeSearch({
|
|
16710
|
+
userId: context.userId,
|
|
16711
|
+
userGroups: context.user.groups ?? [],
|
|
16712
|
+
query,
|
|
16713
|
+
tags,
|
|
16714
|
+
topK: Math.max(maxResults, 6),
|
|
16715
|
+
minScore: 0,
|
|
16716
|
+
embeddingModel,
|
|
16717
|
+
apiKeyTable,
|
|
16718
|
+
dataLakeTags,
|
|
16719
|
+
dataLakeTagPrefixes,
|
|
16720
|
+
logger: context.logger
|
|
16721
|
+
}, { db: {
|
|
16722
|
+
fabfiles: context.db.fabfiles,
|
|
16723
|
+
fabfilechunks: chunkRepo
|
|
16724
|
+
} });
|
|
16725
|
+
if (search.results.length === 0) return null;
|
|
16726
|
+
const ranked = search.results.slice(0, maxResults);
|
|
16727
|
+
const seenFile = /* @__PURE__ */ new Set();
|
|
16728
|
+
const citables = [];
|
|
16729
|
+
for (const r of ranked) {
|
|
16730
|
+
if (seenFile.has(r.fileId)) continue;
|
|
16731
|
+
seenFile.add(r.fileId);
|
|
16732
|
+
citables.push({
|
|
16733
|
+
id: r.fileId,
|
|
16734
|
+
type: "document",
|
|
16735
|
+
title: r.fileName,
|
|
16736
|
+
url: `/opti?mode=datalake&article=${r.fileId}`,
|
|
16737
|
+
description: r.fileTags.filter((t) => !t.startsWith("datalake:")).slice(0, 4).join(", ") || void 0,
|
|
16738
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16739
|
+
status: "complete",
|
|
16740
|
+
metadata: {
|
|
16741
|
+
sourceSystem: "knowledge_base",
|
|
16742
|
+
tags: r.fileTags,
|
|
16743
|
+
relevanceScore: r.score
|
|
16744
|
+
}
|
|
16745
|
+
});
|
|
16746
|
+
}
|
|
16747
|
+
const names = citables.slice(0, 3).map((c) => prettyFileName(c.title));
|
|
16748
|
+
const more = citables.length > 3 ? ` +${citables.length - 3} more` : "";
|
|
16749
|
+
await context.statusUpdate({ promptMeta: { citables } }, `📄 Found ${citables.length} relevant doc(s) in the data lake: ${names.join(", ")}${more}`);
|
|
16750
|
+
context.logger.log(`📚 [semantic] returning ${ranked.length}/${search.results.length} passages from ${citables.length} files (top score ${search.results[0].score.toFixed(3)})`);
|
|
16751
|
+
return formatSemanticResults(ranked);
|
|
16752
|
+
} catch (err) {
|
|
16753
|
+
context.logger.warn("📚 [semantic] KB search failed, falling back to keyword:", err);
|
|
16754
|
+
return null;
|
|
16755
|
+
}
|
|
16756
|
+
}
|
|
15710
16757
|
/**
|
|
15711
16758
|
* Formats fab file search results for LLM consumption
|
|
15712
16759
|
*/
|
|
@@ -15722,160 +16769,35 @@ function formatSearchResults(files) {
|
|
|
15722
16769
|
}
|
|
15723
16770
|
const knowledgeBaseSearchTool = {
|
|
15724
16771
|
name: "search_knowledge_base",
|
|
15725
|
-
implementation: (context) =>
|
|
15726
|
-
|
|
15727
|
-
|
|
15728
|
-
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
const searchResults = await context.db.fabfiles.search(context.userId, query, {
|
|
15738
|
-
tags: tags || [],
|
|
15739
|
-
type: file_type,
|
|
15740
|
-
shared: false
|
|
15741
|
-
}, {
|
|
15742
|
-
page: 1,
|
|
15743
|
-
limit: Math.min(max_results, 10)
|
|
15744
|
-
}, {
|
|
15745
|
-
by: "fileName",
|
|
15746
|
-
direction: "asc"
|
|
15747
|
-
}, {
|
|
15748
|
-
textSearch: true,
|
|
15749
|
-
includeShared: true,
|
|
15750
|
-
userGroups: context.user.groups || [],
|
|
15751
|
-
dataLakeTags,
|
|
15752
|
-
dataLakeTagPrefixes,
|
|
15753
|
-
excludeContent: true
|
|
15754
|
-
});
|
|
15755
|
-
context.logger.log("📚 Knowledge Base Search: Found", searchResults.data.length, "of", searchResults.total, "results. Files:", searchResults.data.map((f) => f.fileName));
|
|
15756
|
-
if (searchResults.data.length > 0) {
|
|
15757
|
-
const citables = searchResults.data.map((file, index) => {
|
|
15758
|
-
const fileTags = (file.tags?.map((t) => t.name) || []).filter((t) => !t.startsWith("datalake:")).slice(0, 4).join(", ");
|
|
15759
|
-
return {
|
|
15760
|
-
id: file.id,
|
|
15761
|
-
type: "document",
|
|
15762
|
-
title: file.fileName,
|
|
15763
|
-
url: `/opti?mode=datalake&article=${file.id}`,
|
|
15764
|
-
description: fileTags || void 0,
|
|
15765
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15766
|
-
status: "complete",
|
|
15767
|
-
metadata: {
|
|
15768
|
-
sourceSystem: "knowledge_base",
|
|
15769
|
-
tags: file.tags?.map((t) => t.name) || [],
|
|
15770
|
-
relevanceScore: 1 - index * .1
|
|
15771
|
-
}
|
|
15772
|
-
};
|
|
15773
|
-
});
|
|
15774
|
-
await context.statusUpdate({ promptMeta: { citables } }, "Knowledge base search results");
|
|
15775
|
-
context.logger.log(`📚 Knowledge Base Search: Stored ${citables.length} citables`);
|
|
16772
|
+
implementation: (context) => {
|
|
16773
|
+
let searchCallCount = 0;
|
|
16774
|
+
const MAX_SEARCHES = 3;
|
|
16775
|
+
return {
|
|
16776
|
+
toolFn: async (value) => {
|
|
16777
|
+
const params = value;
|
|
16778
|
+
await context.onStart?.("search_knowledge_base", params);
|
|
16779
|
+
const { query, tags, file_type, max_results = 5 } = params;
|
|
16780
|
+
searchCallCount++;
|
|
16781
|
+
if (searchCallCount > MAX_SEARCHES) {
|
|
16782
|
+
context.logger.log(`📚 Knowledge Base Search: call #${searchCallCount} — capped, instructing model to answer`);
|
|
16783
|
+
return `You have already run ${searchCallCount - 1} knowledge-base searches; the relevant passages are in the conversation above. STOP searching and compose your complete answer NOW from those results. Do NOT call search_knowledge_base or retrieve_knowledge_content again unless a specific named fact is genuinely missing.`;
|
|
15776
16784
|
}
|
|
15777
|
-
|
|
15778
|
-
|
|
15779
|
-
|
|
15780
|
-
|
|
15781
|
-
}
|
|
15782
|
-
},
|
|
15783
|
-
toolSchema: {
|
|
15784
|
-
name: "search_knowledge_base",
|
|
15785
|
-
description: "Search the user's uploaded knowledge base (fab files). Searches across file names, tags, and notes for broad recall. Returns relevant documents from the user's own files, organization-shared files, and files explicitly shared with them. Use this tool when the user asks about their own documents, uploaded files, or organization knowledge.",
|
|
15786
|
-
parameters: {
|
|
15787
|
-
type: "object",
|
|
15788
|
-
properties: {
|
|
15789
|
-
query: {
|
|
15790
|
-
type: "string",
|
|
15791
|
-
description: "The search query to find relevant documents. Matches against file names, tags, and notes."
|
|
15792
|
-
},
|
|
15793
|
-
tags: {
|
|
15794
|
-
type: "array",
|
|
15795
|
-
items: { type: "string" },
|
|
15796
|
-
description: "Optional: filter results by tag names. Supports partial matching. For optimization docs, use tags like \"opti:family:scheduling\", \"opti:QUBO\", \"opti:solver:highs\". For IonQ sales intelligence, use tags like \"ionq:vertical:pharma\", \"ionq:competitor:ibm\", \"ionq:type:product-specs\", \"ionq:stage:discovery\", \"ionq:offering:forte\". Any matching tag qualifies the file."
|
|
15797
|
-
},
|
|
15798
|
-
file_type: {
|
|
15799
|
-
type: "string",
|
|
15800
|
-
enum: [
|
|
15801
|
-
"pdf",
|
|
15802
|
-
"text",
|
|
15803
|
-
"image",
|
|
15804
|
-
"excel",
|
|
15805
|
-
"word",
|
|
15806
|
-
"json",
|
|
15807
|
-
"csv",
|
|
15808
|
-
"markdown",
|
|
15809
|
-
"code",
|
|
15810
|
-
"url"
|
|
15811
|
-
],
|
|
15812
|
-
description: "Optional: filter results by file type"
|
|
15813
|
-
},
|
|
15814
|
-
max_results: {
|
|
15815
|
-
type: "number",
|
|
15816
|
-
description: "Maximum number of results to return (default: 5, max: 10)",
|
|
15817
|
-
minimum: 1,
|
|
15818
|
-
maximum: 10
|
|
15819
|
-
}
|
|
15820
|
-
},
|
|
15821
|
-
required: ["query"]
|
|
15822
|
-
}
|
|
15823
|
-
}
|
|
15824
|
-
})
|
|
15825
|
-
};
|
|
15826
|
-
const DEFAULT_MAX_CHARS = 8e3;
|
|
15827
|
-
const ABSOLUTE_MAX_CHARS = 16e3;
|
|
15828
|
-
const knowledgeBaseRetrieveTool = {
|
|
15829
|
-
name: "retrieve_knowledge_content",
|
|
15830
|
-
implementation: (context) => ({
|
|
15831
|
-
toolFn: async (value) => {
|
|
15832
|
-
const params = value;
|
|
15833
|
-
await context.onStart?.("retrieve_knowledge_content", params);
|
|
15834
|
-
const { file_id, tags, query, max_chars } = params;
|
|
15835
|
-
const charBudget = Math.min(max_chars ?? DEFAULT_MAX_CHARS, ABSOLUTE_MAX_CHARS);
|
|
15836
|
-
context.logger.log("📖 Knowledge Retrieve: params", {
|
|
15837
|
-
file_id,
|
|
15838
|
-
tags,
|
|
15839
|
-
query,
|
|
15840
|
-
max_chars: charBudget
|
|
15841
|
-
});
|
|
15842
|
-
if (!file_id && !tags?.length && !query) return "Error: You must provide at least one of file_id, tags, or query.";
|
|
15843
|
-
if (!context.db.fabfiles) {
|
|
15844
|
-
context.logger.error("❌ Knowledge Retrieve: fabfiles repository not available");
|
|
15845
|
-
return "Knowledge base retrieval is not available at this time.";
|
|
15846
|
-
}
|
|
15847
|
-
if (!context.db.fabfilechunks) {
|
|
15848
|
-
context.logger.error("❌ Knowledge Retrieve: fabfilechunks repository not available");
|
|
15849
|
-
return "Knowledge base retrieval is not available at this time (chunk reader unavailable).";
|
|
15850
|
-
}
|
|
15851
|
-
try {
|
|
15852
|
-
let files = [];
|
|
15853
|
-
if (file_id) {
|
|
15854
|
-
const ownedFile = await context.db.fabfiles.findByIdAndUserId(file_id, context.userId);
|
|
15855
|
-
if (ownedFile) files = [ownedFile];
|
|
15856
|
-
else {
|
|
15857
|
-
const sharedFile = await context.db.fabfiles.findById(file_id);
|
|
15858
|
-
if (sharedFile && !sharedFile.deletedAt && !sharedFile.archivedAt) {
|
|
15859
|
-
const { dataLakeTags, dataLakeTagPrefixes } = await getDynamicDataLakeAccess(context);
|
|
15860
|
-
const fileTags = sharedFile.tags?.map((t) => t.name) || [];
|
|
15861
|
-
const hasMetaTagAccess = dataLakeTags.some((dlt) => fileTags.includes(dlt));
|
|
15862
|
-
const hasPrefixAccess = dataLakeTagPrefixes.some((p) => fileTags.some((t) => t.startsWith(p)));
|
|
15863
|
-
const hasShareAccess = sharedFile.users?.some((u) => u.userId === context.userId && u.permissions?.some((p) => p === "read" || p === "write"));
|
|
15864
|
-
const userGroups = context.user.groups || [];
|
|
15865
|
-
const hasGroupAccess = userGroups.length > 0 && sharedFile.groups?.some((g) => userGroups.includes(g.groupId) && g.permissions?.some((p) => p === "read" || p === "write"));
|
|
15866
|
-
if (hasMetaTagAccess || hasPrefixAccess || hasShareAccess || hasGroupAccess) files = [sharedFile];
|
|
15867
|
-
}
|
|
15868
|
-
}
|
|
15869
|
-
if (files.length === 0) return `No document found with ID "${file_id}". The file may not exist or you may not have access to it. Try using search_knowledge_base to find the correct file ID.`;
|
|
16785
|
+
context.logger.log("📚 Knowledge Base Search: userId:", context.userId, "query:", query, "tags:", tags);
|
|
16786
|
+
if (!context.db.fabfiles) {
|
|
16787
|
+
context.logger.error("❌ Knowledge Base Search: fabfiles repository not available");
|
|
16788
|
+
return "Knowledge base search is not available at this time.";
|
|
15870
16789
|
}
|
|
15871
|
-
|
|
16790
|
+
const semantic = await trySemanticKbSearch(context, query, tags, max_results);
|
|
16791
|
+
if (semantic) return semantic;
|
|
16792
|
+
try {
|
|
15872
16793
|
const { dataLakeTags, dataLakeTagPrefixes } = await getDynamicDataLakeAccess(context);
|
|
15873
|
-
|
|
16794
|
+
const searchResults = await context.db.fabfiles.search(context.userId, query, {
|
|
15874
16795
|
tags: tags || [],
|
|
16796
|
+
type: file_type,
|
|
15875
16797
|
shared: false
|
|
15876
16798
|
}, {
|
|
15877
16799
|
page: 1,
|
|
15878
|
-
limit:
|
|
16800
|
+
limit: dataLakeTags.length > 0 ? 200 : 50
|
|
15879
16801
|
}, {
|
|
15880
16802
|
by: "fileName",
|
|
15881
16803
|
direction: "asc"
|
|
@@ -15886,87 +16808,251 @@ const knowledgeBaseRetrieveTool = {
|
|
|
15886
16808
|
dataLakeTags,
|
|
15887
16809
|
dataLakeTagPrefixes,
|
|
15888
16810
|
excludeContent: true
|
|
15889
|
-
})
|
|
15890
|
-
|
|
15891
|
-
|
|
15892
|
-
|
|
15893
|
-
|
|
15894
|
-
|
|
15895
|
-
|
|
15896
|
-
|
|
15897
|
-
|
|
15898
|
-
|
|
15899
|
-
|
|
15900
|
-
|
|
15901
|
-
}
|
|
15902
|
-
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
|
|
15906
|
-
|
|
15907
|
-
|
|
15908
|
-
|
|
15909
|
-
|
|
15910
|
-
|
|
16811
|
+
});
|
|
16812
|
+
const queryTerms = Array.from(new Set(query.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 3)));
|
|
16813
|
+
const scoreFile = (file) => {
|
|
16814
|
+
const hay = `${file.fileName} ${(file.tags?.map((t) => t.name) || []).join(" ")} ${file.notes || ""}`.toLowerCase();
|
|
16815
|
+
return queryTerms.reduce((n, term) => hay.includes(term) ? n + 1 : n, 0);
|
|
16816
|
+
};
|
|
16817
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16818
|
+
const rankedResults = searchResults.data.filter((f) => {
|
|
16819
|
+
const key = (f.fileName || f.id || "").toLowerCase();
|
|
16820
|
+
if (seen.has(key)) return false;
|
|
16821
|
+
seen.add(key);
|
|
16822
|
+
return true;
|
|
16823
|
+
}).map((f) => ({
|
|
16824
|
+
f,
|
|
16825
|
+
score: scoreFile(f)
|
|
16826
|
+
})).sort((a, b) => b.score - a.score || a.f.fileName.localeCompare(b.f.fileName)).slice(0, max_results).map((r) => r.f);
|
|
16827
|
+
context.logger.log("📚 Knowledge Base Search: Found", rankedResults.length, "of", searchResults.total, "results (deduped + relevance-ranked). Files:", rankedResults.map((f) => f.fileName));
|
|
16828
|
+
if (rankedResults.length > 0) {
|
|
16829
|
+
const citables = rankedResults.map((file, index) => {
|
|
16830
|
+
const fileTags = (file.tags?.map((t) => t.name) || []).filter((t) => !t.startsWith("datalake:")).slice(0, 4).join(", ");
|
|
16831
|
+
return {
|
|
16832
|
+
id: file.id,
|
|
16833
|
+
type: "document",
|
|
16834
|
+
title: file.fileName,
|
|
16835
|
+
url: `/opti?mode=datalake&article=${file.id}`,
|
|
16836
|
+
description: fileTags || void 0,
|
|
16837
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16838
|
+
status: "complete",
|
|
16839
|
+
metadata: {
|
|
16840
|
+
sourceSystem: "knowledge_base",
|
|
16841
|
+
tags: file.tags?.map((t) => t.name) || [],
|
|
16842
|
+
relevanceScore: 1 - index * .1
|
|
16843
|
+
}
|
|
16844
|
+
};
|
|
16845
|
+
});
|
|
16846
|
+
const prettyName = (fn) => fn.replace(/\.[a-z0-9]+$/i, "").replace(/^\[[^\]]*\]\s*/, "").replace(/^\d+[\s._-]*/, "").replace(/[-_]+/g, " ").trim();
|
|
16847
|
+
const names = rankedResults.slice(0, 3).map((f) => prettyName(f.fileName));
|
|
16848
|
+
const more = rankedResults.length > 3 ? ` +${rankedResults.length - 3} more` : "";
|
|
16849
|
+
const foundStatus = `📄 Found ${rankedResults.length} in the data lake: ${names.join(", ")}${more}`;
|
|
16850
|
+
await context.statusUpdate({ promptMeta: { citables } }, foundStatus);
|
|
16851
|
+
context.logger.log(`📚 Knowledge Base Search: Stored ${citables.length} citables`);
|
|
16852
|
+
} else await context.statusUpdate({}, `📭 No data-lake matches for “${query.length > 50 ? query.slice(0, 49) + "…" : query}” — broadening…`);
|
|
16853
|
+
return formatSearchResults(rankedResults);
|
|
16854
|
+
} catch (error) {
|
|
16855
|
+
context.logger.error("❌ Knowledge Base Search: Error during search:", error);
|
|
16856
|
+
return "An error occurred while searching your knowledge base. Please try again.";
|
|
15911
16857
|
}
|
|
15912
|
-
|
|
15913
|
-
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
|
|
15921
|
-
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
|
|
16858
|
+
},
|
|
16859
|
+
toolSchema: {
|
|
16860
|
+
name: "search_knowledge_base",
|
|
16861
|
+
description: "Semantic search over the user's knowledge base. Ranks documents by MEANING (embeddings) and returns the most relevant passage CONTENT inline — so you can usually answer directly from the results without any further calls. Use a clear natural-language query describing what you need; you do NOT need to know exact tags. Make ONE good search per distinct topic, then compose your answer.",
|
|
16862
|
+
parameters: {
|
|
16863
|
+
type: "object",
|
|
16864
|
+
properties: {
|
|
16865
|
+
query: {
|
|
16866
|
+
type: "string",
|
|
16867
|
+
description: "Natural-language description of what you need (e.g. \"IonQ Aria Forte Tempo product specs, #AQ, gate fidelity, use cases\"). Ranked by semantic similarity — be descriptive."
|
|
16868
|
+
},
|
|
16869
|
+
tags: {
|
|
16870
|
+
type: "array",
|
|
16871
|
+
items: { type: "string" },
|
|
16872
|
+
description: "OPTIONAL narrowing filter — semantic ranking already finds the right docs, so usually omit this. If you do filter, use a real tag (matching is partial + case-insensitive), e.g. \"ionq:vertical:pharma\" or \"ionq:type:product-spec\"."
|
|
16873
|
+
},
|
|
16874
|
+
file_type: {
|
|
16875
|
+
type: "string",
|
|
16876
|
+
enum: [
|
|
16877
|
+
"pdf",
|
|
16878
|
+
"text",
|
|
16879
|
+
"image",
|
|
16880
|
+
"excel",
|
|
16881
|
+
"word",
|
|
16882
|
+
"json",
|
|
16883
|
+
"csv",
|
|
16884
|
+
"markdown",
|
|
16885
|
+
"code",
|
|
16886
|
+
"url"
|
|
16887
|
+
],
|
|
16888
|
+
description: "Optional: filter results by file type"
|
|
16889
|
+
},
|
|
16890
|
+
max_results: {
|
|
16891
|
+
type: "number",
|
|
16892
|
+
description: "Maximum number of results to return (default: 5, max: 10)",
|
|
16893
|
+
minimum: 1,
|
|
16894
|
+
maximum: 10
|
|
15927
16895
|
}
|
|
15928
|
-
}
|
|
16896
|
+
},
|
|
16897
|
+
required: ["query"]
|
|
16898
|
+
}
|
|
16899
|
+
}
|
|
16900
|
+
};
|
|
16901
|
+
}
|
|
16902
|
+
};
|
|
16903
|
+
const DEFAULT_MAX_CHARS = 8e3;
|
|
16904
|
+
const ABSOLUTE_MAX_CHARS = 16e3;
|
|
16905
|
+
const knowledgeBaseRetrieveTool = {
|
|
16906
|
+
name: "retrieve_knowledge_content",
|
|
16907
|
+
implementation: (context) => {
|
|
16908
|
+
let retrieveCallCount = 0;
|
|
16909
|
+
const MAX_RETRIEVES = 2;
|
|
16910
|
+
return {
|
|
16911
|
+
toolFn: async (value) => {
|
|
16912
|
+
const params = value;
|
|
16913
|
+
await context.onStart?.("retrieve_knowledge_content", params);
|
|
16914
|
+
const { file_id, tags, query, max_chars } = params;
|
|
16915
|
+
const charBudget = Math.min(max_chars ?? DEFAULT_MAX_CHARS, ABSOLUTE_MAX_CHARS);
|
|
16916
|
+
retrieveCallCount++;
|
|
16917
|
+
if (retrieveCallCount > MAX_RETRIEVES) {
|
|
16918
|
+
context.logger.log(`📖 Knowledge Retrieve: call #${retrieveCallCount} — capped, instructing model to answer`);
|
|
16919
|
+
return `You have already retrieved ${retrieveCallCount - 1} documents and the content is in the conversation above. STOP retrieving and compose your complete answer NOW from what you have.`;
|
|
16920
|
+
}
|
|
16921
|
+
context.logger.log("📖 Knowledge Retrieve: params", {
|
|
16922
|
+
file_id,
|
|
16923
|
+
tags,
|
|
16924
|
+
query,
|
|
16925
|
+
max_chars: charBudget
|
|
15929
16926
|
});
|
|
15930
|
-
if (
|
|
15931
|
-
|
|
15932
|
-
context.logger.
|
|
16927
|
+
if (!file_id && !tags?.length && !query) return "Error: You must provide at least one of file_id, tags, or query.";
|
|
16928
|
+
if (!context.db.fabfiles) {
|
|
16929
|
+
context.logger.error("❌ Knowledge Retrieve: fabfiles repository not available");
|
|
16930
|
+
return "Knowledge base retrieval is not available at this time.";
|
|
16931
|
+
}
|
|
16932
|
+
if (!context.db.fabfilechunks) {
|
|
16933
|
+
context.logger.error("❌ Knowledge Retrieve: fabfilechunks repository not available");
|
|
16934
|
+
return "Knowledge base retrieval is not available at this time (chunk reader unavailable).";
|
|
15933
16935
|
}
|
|
15934
|
-
|
|
16936
|
+
try {
|
|
16937
|
+
let files = [];
|
|
16938
|
+
if (file_id) {
|
|
16939
|
+
const ownedFile = await context.db.fabfiles.findByIdAndUserId(file_id, context.userId);
|
|
16940
|
+
if (ownedFile) files = [ownedFile];
|
|
16941
|
+
else {
|
|
16942
|
+
const sharedFile = await context.db.fabfiles.findById(file_id);
|
|
16943
|
+
if (sharedFile && !sharedFile.deletedAt && !sharedFile.archivedAt) {
|
|
16944
|
+
const { dataLakeTags, dataLakeTagPrefixes } = await getDynamicDataLakeAccess(context);
|
|
16945
|
+
const fileTags = sharedFile.tags?.map((t) => t.name) || [];
|
|
16946
|
+
const hasMetaTagAccess = dataLakeTags.some((dlt) => fileTags.includes(dlt));
|
|
16947
|
+
const hasPrefixAccess = dataLakeTagPrefixes.some((p) => fileTags.some((t) => t.startsWith(p)));
|
|
16948
|
+
const hasShareAccess = sharedFile.users?.some((u) => u.userId === context.userId && u.permissions?.some((p) => p === "read" || p === "write"));
|
|
16949
|
+
const userGroups = context.user.groups || [];
|
|
16950
|
+
const hasGroupAccess = userGroups.length > 0 && sharedFile.groups?.some((g) => userGroups.includes(g.groupId) && g.permissions?.some((p) => p === "read" || p === "write"));
|
|
16951
|
+
if (hasMetaTagAccess || hasPrefixAccess || hasShareAccess || hasGroupAccess) files = [sharedFile];
|
|
16952
|
+
}
|
|
16953
|
+
}
|
|
16954
|
+
if (files.length === 0) return `No document found with ID "${file_id}". The file may not exist or you may not have access to it. Try using search_knowledge_base to find the correct file ID.`;
|
|
16955
|
+
}
|
|
16956
|
+
if (files.length === 0 && (tags?.length || query)) {
|
|
16957
|
+
const { dataLakeTags, dataLakeTagPrefixes } = await getDynamicDataLakeAccess(context);
|
|
16958
|
+
files = (await context.db.fabfiles.search(context.userId, query || "", {
|
|
16959
|
+
tags: tags || [],
|
|
16960
|
+
shared: false
|
|
16961
|
+
}, {
|
|
16962
|
+
page: 1,
|
|
16963
|
+
limit: 5
|
|
16964
|
+
}, {
|
|
16965
|
+
by: "fileName",
|
|
16966
|
+
direction: "asc"
|
|
16967
|
+
}, {
|
|
16968
|
+
textSearch: true,
|
|
16969
|
+
includeShared: true,
|
|
16970
|
+
userGroups: context.user.groups || [],
|
|
16971
|
+
dataLakeTags,
|
|
16972
|
+
dataLakeTagPrefixes,
|
|
16973
|
+
excludeContent: true
|
|
16974
|
+
})).data;
|
|
16975
|
+
if (files.length === 0) return `No documents found matching ${[query && `query "${query}"`, tags?.length && `tags [${tags.join(", ")}]`].filter(Boolean).join(" and ")}. Try broadening your search with search_knowledge_base.`;
|
|
16976
|
+
}
|
|
16977
|
+
let totalCharsUsed = 0;
|
|
16978
|
+
const sections = [];
|
|
16979
|
+
const retrievedFiles = [];
|
|
16980
|
+
for (const file of files) {
|
|
16981
|
+
if (totalCharsUsed >= charBudget) break;
|
|
16982
|
+
const chunks = await context.db.fabfilechunks.findByFabFileId(file.id);
|
|
16983
|
+
if (chunks.length === 0) {
|
|
16984
|
+
context.logger.log(`📖 Knowledge Retrieve: No chunks for file ${file.fileName} (${file.id})`);
|
|
16985
|
+
continue;
|
|
16986
|
+
}
|
|
16987
|
+
const fullText = chunks.map((c) => c.text).join("\n");
|
|
16988
|
+
const remainingBudget = charBudget - totalCharsUsed;
|
|
16989
|
+
const truncated = fullText.length > remainingBudget;
|
|
16990
|
+
const content = truncated ? fullText.slice(0, remainingBudget) : fullText;
|
|
16991
|
+
const fileTags = file.tags?.map((t) => t.name).join(", ") || "none";
|
|
16992
|
+
const charLabel = truncated ? `${content.length} (truncated from ${fullText.length})` : `${content.length}`;
|
|
16993
|
+
sections.push(`### ${file.fileName} (ID: ${file.id})\nTags: ${fileTags}\nChunks: ${chunks.length} | Characters: ${charLabel}\n---\n` + content);
|
|
16994
|
+
totalCharsUsed += content.length;
|
|
16995
|
+
retrievedFiles.push(file);
|
|
16996
|
+
}
|
|
16997
|
+
if (retrievedFiles.length === 0) return "Found matching documents but they have no indexed content. The files may not have been processed yet.";
|
|
16998
|
+
const citables = retrievedFiles.map((file, index) => {
|
|
16999
|
+
const fileTags = (file.tags?.map((t) => t.name) || []).filter((t) => !t.startsWith("datalake:")).slice(0, 4).join(", ");
|
|
17000
|
+
return {
|
|
17001
|
+
id: file.id,
|
|
17002
|
+
type: "document",
|
|
17003
|
+
title: file.fileName,
|
|
17004
|
+
url: `/opti?mode=datalake&article=${file.id}`,
|
|
17005
|
+
description: fileTags || void 0,
|
|
17006
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17007
|
+
status: "complete",
|
|
17008
|
+
metadata: {
|
|
17009
|
+
sourceSystem: "knowledge_base",
|
|
17010
|
+
tags: file.tags?.map((t) => t.name) || [],
|
|
17011
|
+
relevanceScore: 1 - index * .1
|
|
17012
|
+
}
|
|
17013
|
+
};
|
|
17014
|
+
});
|
|
17015
|
+
if (citables.length > 0) {
|
|
17016
|
+
await context.statusUpdate({ promptMeta: { citables } }, "Knowledge base content retrieved");
|
|
17017
|
+
context.logger.log(`📖 Knowledge Retrieve: Stored ${citables.length} citables`);
|
|
17018
|
+
}
|
|
17019
|
+
return `Retrieved content from ${retrievedFiles.length} of ${files.length} document(s):\n
|
|
15935
17020
|
` + sections.join("\n\n---\n\n");
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
|
|
15939
|
-
|
|
15940
|
-
|
|
15941
|
-
|
|
15942
|
-
|
|
15943
|
-
|
|
15944
|
-
|
|
15945
|
-
|
|
15946
|
-
|
|
15947
|
-
|
|
15948
|
-
|
|
15949
|
-
|
|
15950
|
-
|
|
15951
|
-
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
|
|
15961
|
-
|
|
15962
|
-
|
|
15963
|
-
|
|
15964
|
-
|
|
17021
|
+
} catch (error) {
|
|
17022
|
+
context.logger.error("❌ Knowledge Retrieve: Error during retrieval:", error);
|
|
17023
|
+
return "An error occurred while retrieving document content. Please try again.";
|
|
17024
|
+
}
|
|
17025
|
+
},
|
|
17026
|
+
toolSchema: {
|
|
17027
|
+
name: "retrieve_knowledge_content",
|
|
17028
|
+
description: "Read the actual text content of knowledge base documents. Use this after search_knowledge_base to read documents by file ID, or provide tags/query to find and read documents in one step. Returns the full text content (up to the character budget) for grounding your responses in the user's curated knowledge.",
|
|
17029
|
+
parameters: {
|
|
17030
|
+
type: "object",
|
|
17031
|
+
properties: {
|
|
17032
|
+
file_id: {
|
|
17033
|
+
type: "string",
|
|
17034
|
+
description: "The file ID to retrieve (from search_knowledge_base results). Most efficient for single-document retrieval."
|
|
17035
|
+
},
|
|
17036
|
+
tags: {
|
|
17037
|
+
type: "array",
|
|
17038
|
+
items: { type: "string" },
|
|
17039
|
+
description: "OPTIONAL tag filter (usually unnecessary — search_knowledge_base already returns the content you need). If used, real examples: \"ionq:vertical:pharma\", \"ionq:competitor:ibm\", \"ionq:type:product-spec\"."
|
|
17040
|
+
},
|
|
17041
|
+
query: {
|
|
17042
|
+
type: "string",
|
|
17043
|
+
description: "Search query to find documents. Can be combined with tags for more targeted retrieval."
|
|
17044
|
+
},
|
|
17045
|
+
max_chars: {
|
|
17046
|
+
type: "number",
|
|
17047
|
+
description: "Maximum characters of content to return (default: 8000, max: 16000). Lower values for quick lookups, higher for detailed reading.",
|
|
17048
|
+
minimum: 500,
|
|
17049
|
+
maximum: 16e3
|
|
17050
|
+
}
|
|
15965
17051
|
}
|
|
15966
17052
|
}
|
|
15967
17053
|
}
|
|
15968
|
-
}
|
|
15969
|
-
}
|
|
17054
|
+
};
|
|
17055
|
+
}
|
|
15970
17056
|
};
|
|
15971
17057
|
function formatResult$1(result) {
|
|
15972
17058
|
const lines = [
|
|
@@ -18848,6 +19934,13 @@ z.object({
|
|
|
18848
19934
|
knowledgeIds: z.array(z.string()).optional(),
|
|
18849
19935
|
artifactIds: z.array(z.string()).optional(),
|
|
18850
19936
|
agentIds: z.array(z.string()).optional(),
|
|
19937
|
+
systemPromptText: z.string().optional(),
|
|
19938
|
+
surface: z.string().optional(),
|
|
19939
|
+
enabledTools: z.array(z.string()).optional(),
|
|
19940
|
+
disabledTools: z.array(z.string()).optional(),
|
|
19941
|
+
forceKnowledgeRetrieval: z.boolean().optional(),
|
|
19942
|
+
retrievalTags: z.array(z.string()).optional(),
|
|
19943
|
+
temperature: z.number().optional(),
|
|
18851
19944
|
tags: z.array(z.object({
|
|
18852
19945
|
name: z.string(),
|
|
18853
19946
|
strength: z.number()
|
|
@@ -18857,7 +19950,8 @@ z.object({
|
|
|
18857
19950
|
clonedSourceId: z.string().optional().nullable(),
|
|
18858
19951
|
forkedSourceId: z.string().optional().nullable(),
|
|
18859
19952
|
projectId: z.string().optional(),
|
|
18860
|
-
lastUsedModel: z.string().optional().nullable()
|
|
19953
|
+
lastUsedModel: z.string().optional().nullable(),
|
|
19954
|
+
optiHashi: z.boolean().optional()
|
|
18861
19955
|
});
|
|
18862
19956
|
z.object({ id: z.string() });
|
|
18863
19957
|
z.object({ id: z.string() });
|
|
@@ -23995,6 +25089,7 @@ var ServerLlmBackend = class ServerLlmBackend {
|
|
|
23995
25089
|
let eventCount = 0;
|
|
23996
25090
|
const accumulator = new StreamAccumulator();
|
|
23997
25091
|
let receivedDone = false;
|
|
25092
|
+
let receivedError = false;
|
|
23998
25093
|
const parser = createParser({ onEvent: (event) => {
|
|
23999
25094
|
eventCount++;
|
|
24000
25095
|
streamLogger.onEvent(eventCount, event.data || "");
|
|
@@ -24015,6 +25110,7 @@ var ServerLlmBackend = class ServerLlmBackend {
|
|
|
24015
25110
|
try {
|
|
24016
25111
|
const parsed = JSON.parse(data);
|
|
24017
25112
|
if (parsed.type === "error") {
|
|
25113
|
+
receivedError = true;
|
|
24018
25114
|
streamLogger.onCriticalEvent(eventCount, "ERROR", parsed.message || "Server error");
|
|
24019
25115
|
reject(new Error(parsed.message || "Server error"));
|
|
24020
25116
|
return;
|
|
@@ -24061,6 +25157,10 @@ var ServerLlmBackend = class ServerLlmBackend {
|
|
|
24061
25157
|
parser.feed(chunk.toString());
|
|
24062
25158
|
});
|
|
24063
25159
|
response.data.on("end", () => {
|
|
25160
|
+
if (receivedError) {
|
|
25161
|
+
logger.debug("[ServerLlmBackend] Stream ended after server-sent error event");
|
|
25162
|
+
return;
|
|
25163
|
+
}
|
|
24064
25164
|
if (!receivedDone) {
|
|
24065
25165
|
logger.warn(`[ServerLlmBackend] Stream ended without [DONE] signal. Accumulated text: ${accumulator.accumulatedLength} chars, tools: ${accumulator.toolCount}`);
|
|
24066
25166
|
if (!accumulator.isEmpty()) {
|