Add local machine context snapshots

This commit is contained in:
CCLCK 2026-04-12 18:01:40 +08:00
parent 2e8e378a8c
commit c26a2cf23e
14 changed files with 622 additions and 6 deletions

5
.gitignore vendored
View file

@ -30,6 +30,7 @@ build/
# MCP files
openspace/config/config_mcp.json
.mcp.json
# Logs
logs/
@ -46,6 +47,7 @@ showcase/.openspace/*
# GDPVal benchmark cache
gdpval_bench/.openspace/*
!gdpval_bench/.openspace/*.db
gdpval_bench/results/
# Embedding cache
embedding_cache/
@ -73,3 +75,6 @@ openspace/skills/*
node_modules/
# Frontend local dependency link
frontend/node_modules
# Local scratch
tmp/

View file

@ -0,0 +1,28 @@
## Admin macOS Local Context
This directory keeps machine-specific snapshots that are useful for future
deployment, migration, and debugging on other machines, while avoiding noise in
the repo root.
Included here:
- `mcp/repo-local.mcp.json`
- snapshot of the repo-local MCP wiring that was used during local debugging
- `gdpval_bench/...`
- selected benchmark result snapshots that were useful during local call-rate
and provider-path investigation
Intentional choices:
- absolute local paths are preserved because they are part of the context
- localhost API base values are preserved because they document the local stack
- secrets are not preserved
- any benchmark config copied here has API keys redacted
Intentionally omitted from this snapshot:
- SQLite/WAL benchmark databases
- raw recording directories
- ad hoc `tmp/` scratch files
Those source locations remain local-only and are ignored via `.gitignore`.

View file

@ -0,0 +1,19 @@
{
"clawwork_root": "/tmp/openspace-bench-mp4o3U",
"gdpval_path": null,
"model": "gpt-5.4",
"max_iterations": 20,
"backend_scope": [
"shell"
],
"use_clawwork_productivity": false,
"run_name": "codex_callrate_smoke",
"max_tasks": 3,
"per_occupation": null,
"sectors": null,
"occupations": null,
"task_ids": null,
"record_call_details": true,
"enable_evaluation": false,
"concurrency": 1
}

View file

@ -0,0 +1,19 @@
{
"clawwork_root": "/tmp/openspace-bench-noref-JwyV4l",
"gdpval_path": null,
"model": "gpt-5.4",
"max_iterations": 20,
"backend_scope": [
"shell"
],
"use_clawwork_productivity": false,
"run_name": "codex_callrate_smoke_noref",
"max_tasks": 3,
"per_occupation": null,
"sectors": null,
"occupations": null,
"task_ids": null,
"record_call_details": true,
"enable_evaluation": false,
"concurrency": 1
}

View file

@ -0,0 +1,6 @@
{"task_id": "0112fc9b-c3b2-4084-8993-5a4abb1f54f1", "phase": "phase1", "occupation": "Nurse Practitioners", "sector": "Health Care and Social Assistance", "task_value_usd": 0.0, "status": "error", "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "llm_calls": 0, "cost_usd": 0.0, "wall_time_sec": 0.01, "agent_prompt_tokens": 0, "agent_completion_tokens": 0, "agent_total_tokens": 0, "agent_llm_calls": 0, "call_details": []}, "execution": {"iterations": 0, "tool_calls": 0, "time_sec": 0.01}, "skills": {"before": 2, "after": 2, "new_this_task": 0, "evolved": [], "used": []}, "evaluation": {"has_evaluation": false}, "timestamp": "2026-04-12T02:37:44.273256"}
{"task_id": "02314fc6-a24e-42f4-a8cd-362cae0f0ec1", "phase": "phase1", "occupation": "General and Operations Managers", "sector": "Retail Trade", "task_value_usd": 0.0, "status": "error", "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "llm_calls": 0, "cost_usd": 0.0, "wall_time_sec": 0.0, "agent_prompt_tokens": 0, "agent_completion_tokens": 0, "agent_total_tokens": 0, "agent_llm_calls": 0, "call_details": []}, "execution": {"iterations": 0, "tool_calls": 0, "time_sec": 0.0}, "skills": {"before": 2, "after": 2, "new_this_task": 0, "evolved": [], "used": []}, "evaluation": {"has_evaluation": false}, "timestamp": "2026-04-12T02:37:44.278249"}
{"task_id": "02aa1805-c658-4069-8a6a-02dec146063a", "phase": "phase1", "occupation": "Project Management Specialists", "sector": "Professional, Scientific, and Technical Services", "task_value_usd": 0.0, "status": "error", "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "llm_calls": 0, "cost_usd": 0.0, "wall_time_sec": 0.0, "agent_prompt_tokens": 0, "agent_completion_tokens": 0, "agent_total_tokens": 0, "agent_llm_calls": 0, "call_details": []}, "execution": {"iterations": 0, "tool_calls": 0, "time_sec": 0.0}, "skills": {"before": 2, "after": 2, "new_this_task": 0, "evolved": [], "used": []}, "evaluation": {"has_evaluation": false}, "timestamp": "2026-04-12T02:37:44.282846"}
{"task_id": "0112fc9b-c3b2-4084-8993-5a4abb1f54f1", "phase": "phase1", "occupation": "Nurse Practitioners", "sector": "Health Care and Social Assistance", "task_value_usd": 0.0, "status": "error", "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "llm_calls": 0, "cost_usd": 0.0, "wall_time_sec": 0.0, "agent_prompt_tokens": 0, "agent_completion_tokens": 0, "agent_total_tokens": 0, "agent_llm_calls": 0, "call_details": []}, "execution": {"iterations": 0, "tool_calls": 0, "time_sec": 0.0}, "skills": {"before": 2, "after": 2, "new_this_task": 0, "evolved": [], "used": []}, "evaluation": {"has_evaluation": false}, "timestamp": "2026-04-12T02:38:19.523517"}
{"task_id": "02314fc6-a24e-42f4-a8cd-362cae0f0ec1", "phase": "phase1", "occupation": "General and Operations Managers", "sector": "Retail Trade", "task_value_usd": 0.0, "status": "error", "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "llm_calls": 0, "cost_usd": 0.0, "wall_time_sec": 0.0, "agent_prompt_tokens": 0, "agent_completion_tokens": 0, "agent_total_tokens": 0, "agent_llm_calls": 0, "call_details": []}, "execution": {"iterations": 0, "tool_calls": 0, "time_sec": 0.0}, "skills": {"before": 2, "after": 2, "new_this_task": 0, "evolved": [], "used": []}, "evaluation": {"has_evaluation": false}, "timestamp": "2026-04-12T02:38:19.526964"}
{"task_id": "02aa1805-c658-4069-8a6a-02dec146063a", "phase": "phase1", "occupation": "Project Management Specialists", "sector": "Professional, Scientific, and Technical Services", "task_value_usd": 0.0, "status": "error", "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "llm_calls": 0, "cost_usd": 0.0, "wall_time_sec": 0.0, "agent_prompt_tokens": 0, "agent_completion_tokens": 0, "agent_total_tokens": 0, "agent_llm_calls": 0, "call_details": []}, "execution": {"iterations": 0, "tool_calls": 0, "time_sec": 0.0}, "skills": {"before": 2, "after": 2, "new_this_task": 0, "evolved": [], "used": []}, "evaluation": {"has_evaluation": false}, "timestamp": "2026-04-12T02:38:19.529894"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,23 @@
{
"clawwork_root": "/tmp/openspace-bench-noref-JwyV4l",
"gdpval_path": null,
"model": "gpt-5.4",
"max_iterations": 20,
"backend_scope": [
"shell"
],
"use_clawwork_productivity": false,
"run_name": "codex_callrate_smoke_noref_fix",
"max_tasks": 2,
"per_occupation": null,
"sectors": null,
"occupations": null,
"task_ids": null,
"record_call_details": true,
"llm_kwargs": {
"api_key": "REDACTED",
"api_base": "http://127.0.0.1:8080/v1"
},
"enable_evaluation": false,
"concurrency": 1
}

View file

@ -0,0 +1,2 @@
{"task_id": "0112fc9b-c3b2-4084-8993-5a4abb1f54f1", "phase": "phase1", "occupation": "Nurse Practitioners", "sector": "Health Care and Social Assistance", "task_value_usd": 0.0, "status": "success", "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "llm_calls": 0, "cost_usd": 0.0, "wall_time_sec": 93.54, "agent_prompt_tokens": 0, "agent_completion_tokens": 0, "agent_total_tokens": 0, "agent_llm_calls": 0, "call_details": []}, "execution": {"iterations": 1, "tool_calls": 0, "time_sec": 93.54}, "skills": {"before": 3, "after": 4, "new_this_task": 1, "evolved": [{"name": "clinical-soap-note-from-narrative-enhanced", "origin": "derived", "path": "/Users/admin/PycharmProjects/openspace/openspace/skills/clinical-soap-note-from-narrative-enhanced/SKILL.md"}], "used": ["clinical-soap-note-from-narrative__v0_9ff620af"]}, "evaluation": {"has_evaluation": false}, "timestamp": "2026-04-12T02:50:48.493407"}
{"task_id": "02314fc6-a24e-42f4-a8cd-362cae0f0ec1", "phase": "phase1", "occupation": "General and Operations Managers", "sector": "Retail Trade", "task_value_usd": 0.0, "status": "success", "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "llm_calls": 0, "cost_usd": 0.0, "wall_time_sec": 269.94, "agent_prompt_tokens": 0, "agent_completion_tokens": 0, "agent_total_tokens": 0, "agent_llm_calls": 0, "call_details": []}, "execution": {"iterations": 10, "tool_calls": 10, "time_sec": 269.94}, "skills": {"before": 4, "after": 6, "new_this_task": 2, "evolved": [{"name": "adaptive-document-output", "origin": "captured", "path": "/Users/admin/PycharmProjects/openspace/openspace/skills/adaptive-document-output/SKILL.md"}, {"name": "verify-build-artifacts", "origin": "captured", "path": "/Users/admin/PycharmProjects/openspace/openspace/skills/verify-build-artifacts/SKILL.md"}], "used": []}, "evaluation": {"has_evaluation": false}, "timestamp": "2026-04-12T02:55:18.445041"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,23 @@
{
"mcpServers": {
"openspace_evolution": {
"type": "stdio",
"command": "/Users/admin/PycharmProjects/openspace/.venv/bin/python",
"args": [
"-m",
"openspace.evolution_mcp_server",
"--transport",
"stdio"
],
"env": {
"OPENSPACE_WORKSPACE": "/Users/admin/PycharmProjects/openspace",
"OPENSPACE_HOST_SKILL_DIRS": "/Users/admin/.claude/projects/openspace/skills,/Users/admin/.claude/skills",
"OPENSPACE_MODEL": "gpt-5.4",
"OPENSPACE_LLM_API_BASE": "http://127.0.0.1:8080/v1",
"OPENSPACE_LLM_OPENAI_STREAM_COMPAT": "true",
"OPENSPACE_ENABLE_RECORDING": "false",
"OPENSPACE_BACKEND_SCOPE": "shell,system"
}
}
}
}

View file

@ -388,6 +388,7 @@ def _make_config(cfg: Dict, phase: str, worker_id: int = 0):
return OpenSpaceConfig(
llm_model=cfg["model"],
llm_kwargs=cfg.get("llm_kwargs", {}),
workspace_dir=str(rd / "workspace" / phase),
recording_log_dir=rec_dir,
recording_backends=cfg.get("backend_scope", ["shell", "web"]),
@ -1164,6 +1165,16 @@ async def main(args: argparse.Namespace) -> None:
if getattr(args, "use_clawwork_productivity", False):
cfg["use_clawwork_productivity"] = True
# Match CLI / MCP entrypoints: resolve provider credentials once and pass
# them into OpenSpaceConfig.llm_kwargs so benchmark runs work with custom
# OPENSPACE_LLM_* endpoints too.
from openspace.host_detection import build_llm_kwargs, load_runtime_env
load_runtime_env()
resolved_model, llm_kwargs = build_llm_kwargs(cfg.get("model", ""))
cfg["model"] = resolved_model
cfg["llm_kwargs"] = llm_kwargs
# When using ClawWork productivity tools, ensure livebench is importable
# before OpenSpace.initialize() (ShellSession loads productivity_tools which imports livebench)
if cfg.get("use_clawwork_productivity"):

View file

@ -43,6 +43,7 @@ from .patch import (
create_skill,
fix_skill,
derive_skill,
parse_multi_file_full,
SKILL_FILENAME,
)
from .skill_utils import (
@ -71,6 +72,7 @@ EVOLUTION_FAILED = SkillEnginePrompts.EVOLUTION_FAILED
_SKILL_CONTENT_MAX_CHARS = 12_000 # Max chars of SKILL.md in evolution prompt
_MAX_SKILL_NAME_LENGTH = 50 # Max chars for a skill name (directory name)
_MULTI_FILE_FULL_HEADER_RE = re.compile(r"(?m)^\*\*\*\s*File:\s*")
def _sanitize_skill_name(name: str) -> str:
@ -95,6 +97,88 @@ def _sanitize_skill_name(name: str) -> str:
truncated = truncated[:last_hyphen]
return truncated.strip("-")
def _extract_generated_frontmatter_field(content: str, field_name: str) -> Optional[str]:
"""Extract frontmatter fields from single-file or multi-file FULL outputs."""
direct = _extract_frontmatter_field(content, field_name)
if direct:
return direct
if _MULTI_FILE_FULL_HEADER_RE.search(content):
try:
skill_md = parse_multi_file_full(content).get(SKILL_FILENAME, "")
except Exception:
skill_md = ""
if skill_md:
nested = _extract_frontmatter_field(skill_md, field_name)
if nested:
return nested
# Fall back to the top of the generated text to tolerate minor format drift.
head = "\n".join(content.splitlines()[:40])
match = re.search(
rf"(?im)^\s*{re.escape(field_name)}\s*:\s*(.+?)\s*$",
head,
)
if not match:
return None
value = match.group(1).strip().strip("\"'")
return value or None
def _serialize_multi_file_full(files: Dict[str, str]) -> str:
"""Serialize parsed FULL files back into the ``*** Begin Files`` format."""
ordered_paths = [SKILL_FILENAME]
ordered_paths.extend(path for path in sorted(files) if path != SKILL_FILENAME)
lines = ["*** Begin Files"]
for path in ordered_paths:
if path not in files:
continue
lines.append(f"*** File: {path}")
body = files[path].rstrip("\n")
if body:
lines.append(body)
lines.append("*** End Files")
return "\n".join(lines).strip() + "\n"
def _set_generated_frontmatter_field(content: str, field_name: str, value: str) -> str:
"""Inject or update frontmatter fields in single-file or multi-file FULL outputs."""
if _MULTI_FILE_FULL_HEADER_RE.search(content):
files = parse_multi_file_full(content)
files[SKILL_FILENAME] = _set_frontmatter_field(
files.get(SKILL_FILENAME, ""),
field_name,
value,
)
return _serialize_multi_file_full(files)
return _set_frontmatter_field(content, field_name, value)
def _fallback_captured_skill_name(
direction: str,
category: Optional[SkillCategory],
) -> str:
"""Generate a deterministic fallback skill name from the capture direction."""
hint = (direction or "").strip()
hint = re.split(r"[:.;\n]", hint, maxsplit=1)[0].strip()
hint = re.sub(r"(?i)^capture\s+", "", hint)
hint = re.sub(r"(?i)^a\s+reusable\s+", "", hint)
hint = re.sub(r"(?i)^reusable\s+", "", hint)
hint = re.sub(r"(?i)^(workflow|reference|tool[- ]guide)\s+for\s+", "", hint)
hint = re.sub(r"(?i)^(adding|creating|capturing|building)\s+", "", hint)
hint = re.sub(r"(?i)^(a|an|the)\s+", "", hint)
fallback = _sanitize_skill_name(hint)
if fallback:
return fallback
prefix = (category.value if category else "captured-skill").replace("_", "-")
return f"{prefix}-{uuid.uuid4().hex[:6]}"
_ANALYSIS_CONTEXT_MAX = 5 # Max recent analyses to include in prompt
_ANALYSIS_NOTE_MAX_CHARS = 500 # Per-analysis note truncation
@ -937,15 +1021,26 @@ class SkillEvolver:
new_content, change_summary = _extract_change_summary(new_content)
# Extract name/description from the generated content
new_name = _extract_frontmatter_field(new_content, "name")
new_desc = _extract_frontmatter_field(new_content, "description")
new_name = _extract_generated_frontmatter_field(new_content, "name")
new_desc = _extract_generated_frontmatter_field(new_content, "description")
if not new_name:
logger.warning("CAPTURED: LLM did not produce a valid skill name")
return None
new_name = _fallback_captured_skill_name(
ctx.suggestion.direction,
ctx.suggestion.category,
)
logger.warning(
"CAPTURED: LLM did not produce a valid skill name; using fallback '%s'",
new_name,
)
# Sanitize name (enforce length limit + valid chars)
new_name = _sanitize_skill_name(new_name)
new_content = _set_frontmatter_field(new_content, "name", new_name)
if not new_name:
new_name = _fallback_captured_skill_name(
ctx.suggestion.direction,
ctx.suggestion.category,
)
new_content = _set_generated_frontmatter_field(new_content, "name", new_name)
# Create new skill directory via create_skill (handles multi-file FULL)
skill_dirs = self._registry._skill_dirs
@ -1528,4 +1623,4 @@ class SkillEvolver:
f"better error handling or alternative approaches."
)
return None, ""
return None, ""

View file

@ -357,6 +357,7 @@ class OpenSpace:
self._task_done.clear()
self._last_evolved_skills = [] # Reset per-execution tracking
start_time = asyncio.get_event_loop().time()
execution_time = 0.0
# Use external task_id if provided, otherwise generate one
if task_id is None:
task_id = f"task_{uuid.uuid4().hex[:12]}"
@ -555,6 +556,9 @@ class OpenSpace:
}
finally:
if execution_time <= 0:
execution_time = asyncio.get_event_loop().time() - start_time
recording_dir = None
if self._recording_manager and self._recording_manager.recording_status:
recording_dir = self._recording_manager.trajectory_dir

View file

@ -0,0 +1,79 @@
from openspace.skill_engine.evolver import (
_extract_generated_frontmatter_field,
_fallback_captured_skill_name,
_set_generated_frontmatter_field,
)
def test_extract_generated_frontmatter_field_from_single_file() -> None:
content = """---
name: safe-file-write
description: Write files with validation.
---
# Safe File Write
"""
assert _extract_generated_frontmatter_field(content, "name") == "safe-file-write"
assert (
_extract_generated_frontmatter_field(content, "description")
== "Write files with validation."
)
def test_extract_generated_frontmatter_field_from_multi_file_full() -> None:
content = """*** Begin Files
*** File: SKILL.md
---
name: local-acceptance-entry
description: Run a repo's backend and frontend together for review.
---
# Local Acceptance Entry
*** File: examples/start.sh
#!/usr/bin/env bash
echo start
*** End Files
"""
assert (
_extract_generated_frontmatter_field(content, "name")
== "local-acceptance-entry"
)
assert (
_extract_generated_frontmatter_field(content, "description")
== "Run a repo's backend and frontend together for review."
)
def test_set_generated_frontmatter_field_updates_skill_md_in_multi_file_full() -> None:
content = """*** Begin Files
*** File: SKILL.md
---
description: Run a repo's backend and frontend together for review.
---
# Local Acceptance Entry
*** File: examples/start.sh
#!/usr/bin/env bash
echo start
*** End Files
"""
updated = _set_generated_frontmatter_field(content, "name", "local-acceptance-entry")
assert "*** File: SKILL.md" in updated
assert "name: local-acceptance-entry" in updated
assert "*** File: examples/start.sh" in updated
def test_fallback_captured_skill_name_uses_direction_signal() -> None:
direction = (
"Capture a reusable workflow for adding a local acceptance entry in "
"split backend/frontend repos: create one canonical foreground acceptance script."
)
fallback = _fallback_captured_skill_name(direction, None)
assert fallback.startswith("local-acceptance-entry-in-split-backend")
assert len(fallback) <= 50