From fdbb1061f753f289ed12395dba4e9bf3a50d0509 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 01:28:58 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt=20&=20=F0=9F=9B=A1=EF=B8=8F=20?= =?UTF-8?q?Sentinel:=20Improve=20performance,=20security,=20and=20CLI=20in?= =?UTF-8?q?teractive=20experience?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR enhances JeanBot across multiple domains: - ⚡ Bolt: Optimized synthetic embedding generation in `packages/ai` by using `crypto.hash`, `Math.round` for vector math, and centralized normalization. - 🛡️ Sentinel: Hardened `LocalJsonStore` against path traversal in `packages/documents` by strictly filtering key segments and ensuring prefix-based path resolution. - 🚀 Deepened the Python Mission Runner CLI with real-time step updates, new commands (`status`, `plan`, `artifacts`), and improved session stability. Verified with `vitest` and `pytest`. Co-authored-by: hackerxj2010 <198651211+hackerxj2010@users.noreply.github.com> --- packages/ai/src/index.ts | 23 +++++----- packages/documents/src/index.ts | 15 +++++-- src/cognitive/cli.py | 47 ++++++++++++++++++-- src/cognitive/executor.py | 8 ++++ src/cognitive/service.py | 2 + workspace/users/{userId}/.jeanbot/context.md | 6 +-- 6 files changed, 80 insertions(+), 21 deletions(-) diff --git a/packages/ai/src/index.ts b/packages/ai/src/index.ts index 6eb88fc..cb0be57 100644 --- a/packages/ai/src/index.ts +++ b/packages/ai/src/index.ts @@ -10,30 +10,30 @@ const OPENAI_BATCH_SIZE = 32; const MAX_CONCURRENCY = 3; const MAX_RETRIES = 2; -const normalizeText = (value: string) => value.replace(/\s+/g, " ").trim(); +export const normalizeText = (value: string) => value.replace(/\s+/g, " ").trim(); -const contentHashFor = (value: string) => - crypto.createHash("sha256").update(normalizeText(value)).digest("hex"); +export const contentHashFor = (value: string) => + crypto.hash("sha256", normalizeText(value)); -const seededUnitValue = (seed: string, index: number) => { - const digest = crypto.createHash("sha256").update(`${seed}:${index}`).digest(); +export const seededUnitValue = (seed: string, index: number) => { + const digest = crypto.hash("sha256", `${seed}:${index}`, "buffer"); const int = digest.readUInt32BE(0); return int / 0xffffffff; }; -const syntheticVector = (text: string, dimensions = DEFAULT_EMBEDDING_DIMENSIONS) => { +export const syntheticVector = (text: string, dimensions = DEFAULT_EMBEDDING_DIMENSIONS) => { const normalized = normalizeText(text); const hash = contentHashFor(normalized); const values = Array.from({ length: dimensions }, (_, index) => { const centered = seededUnitValue(hash, index) * 2 - 1; - return Number(centered.toFixed(8)); + return Math.round(centered * 1e8) / 1e8; }); return normalizeVector(values); }; -const normalizeVector = (values: number[]) => { +export const normalizeVector = (values: number[]) => { if (values.length === 0) { return values; } @@ -43,7 +43,8 @@ const normalizeVector = (values: number[]) => { return values.map(() => 0); } - return values.map((value) => Number((value / magnitude).toFixed(8))); + const inverseMagnitude = 1 / magnitude; + return values.map((value) => Math.round(value * inverseMagnitude * 1e8) / 1e8); }; const toEmbeddingVectorRecord = ( @@ -52,7 +53,7 @@ const toEmbeddingVectorRecord = ( provider: EmbeddingProvider, model: string ): EmbeddingVectorRecord => ({ - values: normalizeVector(values), + values: provider === "synthetic" ? values : normalizeVector(values), dimensions: values.length, provider, model, @@ -127,7 +128,7 @@ const callOpenAiEmbeddings = async ( const data = payload.data ?? []; return data .sort((left, right) => (left.index ?? 0) - (right.index ?? 0)) - .map((record) => normalizeVector(record.embedding ?? [])); + .map((record) => record.embedding ?? []); }; const embedBatchLive = async ( diff --git a/packages/documents/src/index.ts b/packages/documents/src/index.ts index afd98de..52527df 100644 --- a/packages/documents/src/index.ts +++ b/packages/documents/src/index.ts @@ -48,13 +48,22 @@ export class LocalJsonStore { } private toPath(key: string) { - const relative = key + const segments = key .split("/") - .filter(Boolean) + .filter((s) => s && s !== "." && s !== ".."); + + const relative = segments .map(sanitizeSegment) .join(path.sep); - return path.join(this.baseDirectory, `${relative}.json`); + const absoluteBase = path.resolve(this.baseDirectory); + const absoluteResolved = path.resolve(absoluteBase, `${relative}.json`); + + if (!absoluteResolved.startsWith(absoluteBase)) { + throw new Error(`Invalid storage key: ${key}`); + } + + return absoluteResolved; } private readPath(filePath: string) { diff --git a/src/cognitive/cli.py b/src/cognitive/cli.py index 2f2443b..3a62d2d 100644 --- a/src/cognitive/cli.py +++ b/src/cognitive/cli.py @@ -42,7 +42,16 @@ async def run_shell(args: argparse.Namespace): except ImportError: pass - service = MissionExecutorService(workspace_root=args.workspace_root, mode=args.mode) + def on_step_update(step_id: str, status: str): + color = "\033[92m" if status == "completed" else "\033[94m" + reset = "\033[0m" + print(f" [{color}{status.upper()}{reset}] {step_id}") + + service = MissionExecutorService( + workspace_root=args.workspace_root, + mode=args.mode, + on_step_update=on_step_update, + ) print(f"JeanBot interactive shell ({args.mode} mode)") print(f"Workspace: {args.workspace_root} ({args.workspace_id})") print("Type 'exit' or 'quit' to end session. Type 'help' for commands.") @@ -65,6 +74,9 @@ async def run_shell(args: argparse.Namespace): print("Commands:") print(" help Show this help") print(" history Show command history") + print(" status Show status of the last mission") + print(" plan Show plan of the last mission") + print(" artifacts List artifacts from the last mission") print(" exit | quit Exit shell") print(" Plan and execute a mission") print(" refine Refine the last mission result with feedback") @@ -75,6 +87,34 @@ async def run_shell(args: argparse.Namespace): print(f" {i:3} {cmd}") continue + if line.lower() == "status": + if not last_result: + print("No mission has been run yet.") + else: + print(f"Last Mission Status: {last_result.status}") + print(f"Summary: {last_result.verification_summary}") + print(f"Steps: {len(last_result.step_reports)}") + continue + + if line.lower() == "plan": + if not last_result: + print("No mission has been run yet.") + else: + print(f"Last Mission: {last_result.mission_id}") + for report in last_result.step_reports: + status_char = "✓" if report.diagnostics and report.diagnostics.failure_class == "none" else "✗" + print(f" {status_char} {report.step_id}: {report.summary[:60]}...") + continue + + if line.lower() == "artifacts": + if not last_result: + print("No mission has been run yet.") + else: + print(f"Artifacts ({len(last_result.artifacts)}):") + for artifact in last_result.artifacts: + print(f" - {artifact.title}: {artifact.path}") + continue + if line.lower().startswith("refine "): if not last_result: print("Nothing to refine. Run a mission first.") @@ -104,13 +144,12 @@ async def run_shell(args: argparse.Namespace): print(f"Summary: {last_result.verification_summary}") if last_result.artifacts: print(f"Artifacts: {len(last_result.artifacts)}") - for artifact in last_result.artifacts: - print(f" - {artifact.title}: {artifact.path}") except KeyboardInterrupt: print("\nInterrupt received, type 'exit' to quit.") except Exception as e: - print(f"\nError: {e}") + print(f"\nError during mission execution: {e}") + print("The shell remains active. You can try a different command or objective.") async def run_command(args: argparse.Namespace) -> dict: diff --git a/src/cognitive/executor.py b/src/cognitive/executor.py index 1343686..987ed1e 100644 --- a/src/cognitive/executor.py +++ b/src/cognitive/executor.py @@ -467,6 +467,7 @@ def __init__( sub_agent_service: SubAgentService, file_service: FileService, policy_service: PolicyService, + on_step_update: Any | None = None, ): self.runtime = runtime self.memory_service = memory_service @@ -474,6 +475,7 @@ def __init__( self.sub_agent_service = sub_agent_service self.file_service = file_service self.policy_service = policy_service + self.on_step_update = on_step_update self.intelligence = MissionExecutionIntelligence() self.replanner = AdaptiveReplanner() @@ -869,6 +871,9 @@ async def _execute_step( ) -> StepOutcome: step_started_at = utc_now_iso() step.status = "running" + + if self.on_step_update: + self.on_step_update(step.id, "started") await self.audit_service.record( "mission.step.started", @@ -915,6 +920,9 @@ async def _execute_step( ) step.status = "completed" + + if self.on_step_update: + self.on_step_update(step.id, "completed") report = StepExecutionRecord( step_id=sub_agent_result.step_report.step_id, diff --git a/src/cognitive/service.py b/src/cognitive/service.py index d6ff93d..7a8b2ac 100644 --- a/src/cognitive/service.py +++ b/src/cognitive/service.py @@ -56,6 +56,7 @@ class MissionExecutorService: capability_risk: dict[str, str] = field(default_factory=dict) failure_policy: dict[str, int] = field(default_factory=dict) mode: str = "local" + on_step_update: Any | None = None def build_bundle(self, mission_payload: dict[str, Any]) -> MissionExecutionBundle: workspace_id = mission_payload.get("workspace_id") or mission_payload.get("workspaceId") @@ -112,6 +113,7 @@ def build_bundle(self, mission_payload: dict[str, Any]) -> MissionExecutionBundl sub_agent_service=subagent_service, file_service=file_service, policy_service=policy_service, + on_step_update=self.on_step_update, ) return MissionExecutionBundle( record=record, diff --git a/workspace/users/{userId}/.jeanbot/context.md b/workspace/users/{userId}/.jeanbot/context.md index 207eb92..8bf679d 100644 --- a/workspace/users/{userId}/.jeanbot/context.md +++ b/workspace/users/{userId}/.jeanbot/context.md @@ -1,7 +1,7 @@ # JeanBot User Context -- Current mission: Smoke test -- Updated at: 2026-03-13T21:07:03.733Z -- Completed steps: Inspect workspace files | Load and update memory context | Run policy and risk review | Decompose objective into steps | Create safety checkpoint | Handle finance-sensitive workflows | Synthesize final mission result | Track status and coordination | Synthesize final mission result | Clarify mission constraints | Produce mission documentation +- Current mission: API mission +- Updated at: 2026-05-28T01:24:35.331Z +- Completed steps: Inspect workspace files | Load and update memory context | Run policy and risk review | Decompose objective into steps | Synthesize final mission result | Track status and coordination | Synthesize final mission result | Clarify mission constraints | Produce mission documentation - In-progress steps: none - Upcoming steps: none \ No newline at end of file