Supersede note (2026-10-16, Phase 12): the Hindsight recommendation in §6 below is withdrawn — Hindsight's Postgres + background loop is too heavy for a coding harness, and OpenViking (evaluated as the alternative) was rejected over its server requirement and AGPL license. Strands native memory stays the only backend. See
.planning/phases/12-hindsight-removal/12-RESEARCH.md.
Goal: turn this fork (CsabaConsulting/strands-code, forked from
aws-samples/sample-strands-code-agent v0.4.0) into a Claude Code / Codex CLI
competitor built on the Strands harness, while preserving its existing strengths.
Method: (1) repo inventory of strands_code_agent/ + tests/ +
pyproject.toml; (2) Tavily deep research (tvly research --model pro) on
Claude Code, Codex CLI, Muse Code; (3) Tavily deep research on Strands-native
session/memory/model/skills plus memsearch vs Hindsight vs local markdown.
Strategy: use Strands-native features first; only build genuine CLI gaps.
"Strands Harness" is the umbrella monorepo
(strands-agents/harness-sdk: Python SDK + TypeScript SDK + docs + supporting
packages), not a separate SDK. The "Strands Agents SDK" is the agent loop
inside it. So "leverage the Harness" = use the Python strands-agents package
(+ strands-agents-tools) we already depend on, rather than reinventing loop,
session, memory, model, hook, or tracing primitives.
CodeAgent(Agent)(strands_code_agent/code_agent.py:39, wiring131-149): auto-registerspython_repl+ assembles system prompt. Keep — this is our differentiator (code-generation-first vs plain tool-calling).Toolkit(toolkits.py): libraries / initialization_code / usage_instructions / domain_specific_code aggregated into authorized imports, preamble, prompt sections (code_agent.py:91-115). Keep as the code-preamble mechanism.- Pluggable
PythonInterpreterbackends (local_sandboxed.py,local_exec.py,agentcore.py;README.md:123-163): keep all three. CodeAgentCallbackHandler(callback_handler.py:32-80, default atcode_agent.py:36): keep terminal rendering.- Knowledge/OKF module (
knowledge/bundle.py,concept.py,search.py): keep for large-document navigation. - Solution user-agent registration (
__init__.py:1): keep housekeeping.
Strands separates three things (do not conflate them):
- Session management = resume where you left off (full conversation + agent
state). Built-in:
SessionManagerabstraction,FileSessionManager/S3SessionManager/ snapshot managers, default local-file storage; plusAgentCoreMemorySessionManager(STM conversation persistence + LTM strategies for preferences/facts/summaries, with flush/close semantics). - Conversation management = staying inside the context window during a session.
- Memory (
MemoryManager+MemoryStore) = durable knowledge across sessions. Built-in stores include AgentCore Memory store, Bedrock Knowledge Base store, Zep store; the manager exposessearch_memory/add_memorytools with background extraction (default ~every 5 turns) and aflush()API.
Our gap is purely wiring: CODE_AGENT_INSTRUCTIONS currently states the
interpreter "resets completely with each new user message". Fix = pass a
session_manager (file-based locally, AgentCore in AWS) and a
memory_manager into CodeAgent.__init__ instead of building custom memory.
CLI work remaining (genuinely ours): actor identity + --session-id routing,
session start/end + reclaim policies, flush() before shutdown, namespace /
writable-store policy, local-dev fallback. No custom vector DB needed for v1.
The SDK is provider-agnostic: same agent code runs against Bedrock (default),
Nova, Anthropic, OpenAI (+Responses API), Google, Cohere, Mistral, LlamaAPI,
LiteLLM, Ollama, SageMaker, Writer, Crusoe, Fireworks, OpenRouter, Nebius, OVH,
plus community vLLM / xAI / SGLang / MLX / NVIDIA NIM and custom providers
(pip install 'strands-agents[bedrock|anthropic|openai|...]'; switch provider
class, no agent rewrite). The earlier "HAS NONE" note was overstated: the SDK
has it, CodeAgent just never exposes it (model only via **kwargs).
Fix = /model picker + config file selecting the provider class/credentials,
defaulting to Bedrock; subagent-model override later. Effort: S/M, all CLI UX.
- Skills ARE native:
AgentSkills(skills="./skills/")plugin gives on-demand domain instructions without bloating the system prompt (takes a tool call to activate). KeepToolkitfor code-preamble config; addAgentSkillsfor markdown instruction packs (this is the skill/plugin story you want). - Slash commands are NOT native: no built-in slash parser. Build a thin
command registry + parser (leading
/) on top of Strands hooks (AgentInitialized,MessageAdded, memory hook providers). Start with/model /resume /compact /clear /memory /cost /diff /review. - Modes/effort are NOT native as UX, but primitives exist: turn limits, token budgets, cancellation, stop reasons, streaming. Implement effort modes as CLI presets over model params + tool/step budgets (e.g. fast = cheaper model, fewer steps; thorough = stronger model, higher budgets).
In competitors it is just a context selector: fuzzy file/line picker
(@-mention files, /add), sometimes a Chrome/VS Code companion that points
the agent at open tabs. For our CLI the equivalent is a terminal fuzzy picker
for files/symbols to attach to the prompt — no browser or IDE extension
required for v1.
Native: guardrails, Human-in-the-Loop approval modes (CLI/web/custom),
AgentCore session isolation + CloudWatch observability, HiddenLayer
prompt-injection integration, tracing (OpenTelemetry). Ours to build:
permissions policy file (deny lists, blockReadsOutsideDirs), env scrubbing,
ask/allow prompts before edits/network, sandbox presets around the existing
SandboxedPythonInterpreter allowlist. Your imports.py auto-authorize +
sandbox timeout is the right seed — promote it to an explicit policy.
All three plug into MemoryManager/MemoryStore or plain @tool tools —
none replaces Strands memory; they are store choices:
- Simple markdown (OpenClaw-style, e.g. Memweave pattern: markdown + SQLite
FTS5/BM25 ± local embeddings): 0.5–2 days, zero infra, full data residency,
strong lexical recall for filenames/literals. Best default for solo/local and
offline. Implement as a local
MemoryStoreadapter. - Hindsight (
hindsight-strands, MIT + Cloud pay-as-you-go):retain/recall/reflecttools, TEMPR fusion (semantic + BM25 + graph + temporal),hindsight-coding-agentsauto-ingests repo/git history. ~1 day install, 2–4 days for git auto-ingest. Best for small/medium teams wanting temporal reasoning ("what changed recently") with minimal glue. Watch API-key scoping/RBAC on older tiers vs newer bank-scoped keys. - memsearch (Zilliz, markdown source of truth + Milvus hybrid dense+BM25+RRF, CLI + Python API): 1–2 day prototype, 3–5 days production. Best for high-QPS/multi-tenant production needing RBAC, namespaces, CMK, data-residency and Zilliz Cloud scaling. Costs: self-host ops or per-vector/CU billing.
Recommendation: tiered — (a) ship local markdown/file-session default now
(zero deps, matches CLI offline story); (b) offer Hindsight as opt-in team
memory (pip install hindsight-strands, bank per repo); (c) offer
memsearch/Milvus as the enterprise backend. All behind the same
MemoryStore interface so users switch without agent rewrites.
Correction to the analysis above: strands-agents (PyPI v1.56, source
strands-py/ in the strands-agents/harness-sdk monorepo) is the low-level
SDK — loop plus primitives. strands-harness (PyPI v0.1.1, source
harness-py/ in the same monorepo) is the higher-level, batteries-included
agent built on top of it: create_harness() composes model loop, tools,
just-in-time context, sessions, hooks, and memory into tested defaults with a
tuned system prompt, and returns a plain strands.Agent (every default
overridable). Going lower level (raw SDK primitives) is the wrong direction
for our feature list — it means rebuilding what the harness already ships:
- File/shell tools (
read/write/edit, shell, web,programmatic_tool_callersandbox) — replaces "route everything throughpython_repl" (P0-1). - Sessions on by default (
./.agent/sessions, resume by id) + long-term memory distilled to markdown (./.agent/memory) (P0-5). - Skills loading (
./.agent/skills) +todos/environmentplugins + helper subagent with checklist (P1: skills, subagents). - Context management (truncate bulky tool results ~1500 tokens, compact at
~85%, offload to storage, prompt caching) +
effort="auto|off|minimal|low| medium|high|xhigh|max"presets (P1 context, effort modes). interventions="ask"|"smart"|policy|.cedartool-call gating (security P0-6).mcp_servers,builtin_toolspinning, background-task policy, model"provider/name"strings across Bedrock/Anthropic/OpenAI/Google/Ollama/LiteLLM (P0-4, P0-7). AstrandsCLI (@strands-agents/cli) wraps the same agent — useful reference for our CLI UX.
Architecture: keep CodeAgent(Agent) subclass as the differentiator, but
construct it from harness defaults instead of a bare Agent — e.g. pass our
python_repl tool, Toolkit preamble/instructions, and OKF hooks through
create_harness(tools=..., instructions=..., plugins=..., memory=..., session=...), or build kwargs via harness_agent_kwargs_from_config() and
splat into CodeAgent(...). Dependencies stay loose on purpose (experimental
phase, harness moving fast): strands-agents>=0.1.0 unchanged,
strands-harness>=0.1 added with no upper pin (see pyproject.toml).
Lean-vs-configurable, per the harness configuration reference: create_harness
takes string flags for the common case (context_manager="auto"|"agentic"|False,
memory=True|{dir, stores}|False, effort, caching, interventions,
session, skills) — that is the lean path. Anything deeper is a passthrough:
unnamed kwargs go straight to the Agent constructor, so memory_manager=...
overrides memory, a custom ContextManager (built-in modes, presets like
proactive_summarization/large_tool_offloading/overflow_protection/
stale_tool_cleanup, or fully custom strategies) overrides context_manager,
and memory={"stores": [...]} swaps the backend while keeping harness policy.
The harness also exports its building blocks (resolve_memory,
build_system_prompt, resolve_interventions, HARNESS_CONTRACT) for
compose-with-SDK. Note the whole context-management surface is flagged
Experimental upstream, so expect churn and keep our wrapper thin. Genuinely-ours
work shrinks to: slash-command registry, permissions policy UX, fuzzy file
picker, /model//cost//review commands, memory-tier backends (Hindsight /
memsearch adapters), and the code-first REPL/Toolkit/OKF layer itself.
Do NOT re-fork from strands-agents/harness-sdk. The two repos are different
layers: this fork (~3k lines incl. tests) is the product/differentiator
(CodeAgent code-first loop, Toolkit, sandboxed/AgentCore interpreters, OKF
knowledge, Rich callback) — none of which exists in the monorepo. The monorepo
is the platform (Python + TypeScript SDKs, harness-py/harness-ts packages,
WASM bridge, docs site, MCP server, strandly dev CLI). Forking it means
owning SDK maintenance and cross-language CI, and you would still have to port
the sample's code over. Correct setup: keep this fork as the product repo, add
strands-harness as a dependency (and raise strands-agents to >=1.x), and
treat strands-cli/harness sources as read-only reference (sparse checkout,
not a fork). Only fork the monorepo if you need to change SDK loop internals
upstream won't accept — not our case.
AgentCore stays an opt-in backend, never the default path. Rationale: AgentCore
(configuration: memory IDs, actor provisioning, IAM, regions, runtime billing)
couples the CLI to AWS control-plane resources, while everything it offers has
a portable equivalent behind the same Strands interface (file/S3 session
managers, MemoryStore adapters, local interpreters). Concretely:
- Keep
python_environments/agentcore.py(already isolated + tested) but default to local interpreters; select via config, with lazyboto3import and graceful fallback when AWS is absent. - Prefer the harness's local defaults (
./.agent/sessions,./.agent/memorymarkdown) overAgentCoreMemorySessionManager; reach AgentCore Memory only through theMemoryStore/memory={"stores": [...]}seam when the user opts in. - Bedrock remains a first-class choice (LLM provider string, KB memory store,
optional embeddings/rerank) but never load-bearing: embeddings default to
local (e.g. ONNX via memsearch) and the CLI's default model must be
override-friendly, since the harness default is Bedrock (needs AWS creds).
Note the existing
agentcore = ["boto3..."]optional extra inpyproject.tomlalready models this pattern — extend it, don't harden it.
- Session wiring:
session_manager(file locally / AgentCore in AWS) +--session-id+ flush-on-exit. Removes the "resets each message" limit. - File/edit/Bash surface: prefer
strands-agents-tools(file_read,shell, file editor equivalents) +/diffviewer over routing everything throughpython_repl. - Permissions/HITL: policy file + approval prompts + env scrub (native HITL + guardrails underneath).
/modelpicker + provider config (native providers underneath).- Slash registry +
AgentSkills("./skills/")loader; port 1–2 Toolkits to Skills as proof. - Memory tiers: local markdown store → Hindsight opt-in → memsearch/Milvus
adapter; add
/memory /compact /resumecommands + extraction-cadence policy. - Subagents (agents-as-tools pattern), context commands,
/cost+ tracing/telemetry, then P2 polish (worktree PR flow, pickers, security-review).
- P0: tools+diff (use strands-tools), slash commands (ours), hooks (Strands
hook framework + our parsers), MCP (SDK supports MCP servers as tools —
wire
--mcp-config), session/memory (native managers + our routing), permissions (native HITL/guardrails + our policy), models (native providers- our picker).
- P1: subagents (agents-as-tools pattern), skills (native
AgentSkills), context commands (ours over conversation/session managers), observability (native OTel tracing + our/cost/metrics). - P2: effort presets, worktree PR flow, fuzzy file picker, security-review.