Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ packages/agent-core/src/
├── agents/tool-filter.test.ts # Architecture coverage for definition-based capability filtering
├── agents/query/ # runLlmStream + tool execution cycle (max 50 steps), doom detection
├── agents/query/loop-hooks.ts # 4 hook points: beforeModelBuild, beforeModelCall, afterStepEnd, afterLoopEnd
├── agents/query/hooks/ # auto-compact, auto-inject-reminder, title-generation, todo-continuation, memory-extraction, memory-consolidation
├── agents/query/hooks/ # auto-compact, auto-inject-reminder, title-generation, todo-continuation
├── execution/session-execution-manager.ts # Sole logical Execution lifecycle/admission, run resources, abort, recovery, and terminal owner
├── process/ # ProcessRunner lifecycle, bounded streaming, timeout/abort, and structured results
├── tools/define-tool.ts # defineTool() → ToolDescriptor (strict RawToolResult + explicit outputPolicy)
Expand All @@ -188,11 +188,11 @@ packages/agent-core/src/
├── tools/riipgrep/ # Ripgrep wrapper for search tools
├── core/ # register-tools.ts: wires tools and finalized-result audit/logger hooks
├── store/ # Zustand vanilla store: createSessionStore, StreamEvent reducer, ModelMessage projection, persist/load
├── background/ # BackgroundTaskManager (fire-and-forget, dedup) + tasks: title-generation, memory-extraction, memory-consolidation
├── background/ # BackgroundTaskManager (fire-and-forget, dedup) + title-generation task
├── commands/ # CommandRegistry + /compact command
├── compression/ # DCP-like dynamic range compression: model tool action, refs, block state, soft/strong nudges below hard threshold
├── compact/ # Mandatory hard compact safety path at >=85% context pressure plus /compact command
├── memory/ # MemoryFileManager (atomic writes, frontmatter, index), schemas, types, constants
├── memory/ # MemoryService, Markdown adapter, idle learning coordinator, policy, schemas, limits
├── session-goal/ # Session.goal schema, ownership service, status, budget, and usage
├── hitl/ # Durable project-scoped approval/question queue and redacted display payloads
├── automations/ # Canonical Automation schemas, schedule, durable Invocation, Session dispatch
Expand Down Expand Up @@ -420,9 +420,13 @@ All six implement `Agent`: `store: StoreApi<SessionStoreState>`, `run(options)
beforeModelBuild (auto-compact) → toModelMessages → beforeModelCall (auto-inject-reminder)
→ runLlmStream → consumeFullStream → afterStepEnd (todo-continuation)
→ executeToolCalls (doom detection → partition → guards → execute)
→ afterLoopEnd (todo-continuation, memory-extraction, memory-consolidation)
→ afterLoopEnd (todo-continuation)
```

Successful root Lead/Discussion terminals update the durable Memory cursor;
`MemoryIdleCoordinator` performs automatic learning outside the Query Loop after
10 minutes of inactivity.

## Tool System

**35+ builtin tools** (base tools via `createBuiltinToolDescriptors()`, memory, Goal, Automation, Project Todo, and GitHub connector tools — all registered in `core/register-tools.ts`):
Expand Down Expand Up @@ -457,7 +461,7 @@ ArchCode has two intentionally separate context-reduction paths. Dynamic DCP-lik

## Memory System

Project: `.archcode/runtime/memory/`, User: `~/.archcode/memory/` (user-global, not under project runtime). Structure: `index.md` (topic index), `preferences.md`, `knowledge/{topic}.md` (frontmatter + markdown). Types: `"user" | "feedback" | "project" | "reference"`. `MemoryFileManager`: atomic writes, path validation, frontmatter parse/format, index rebuild/search. Extraction (background task via `runLlmObject`) → writes topics. Consolidation (background task) → reorganizes index. Injection: ConfiguredAgent resolves one immutable Execution snapshot; PromptContractCompiler labels it non-authoritative and emits its source/status in the durable Prompt trace. `memory_write` rejects secrets.
Project: `.archcode/runtime/memory/`, User: `~/.archcode/memory/` (user-global, not under project runtime). Structure: `index.md` (generated topic index), `preferences.md`, `knowledge/{topic}.md` (frontmatter + Markdown). Types: `"user" | "feedback" | "project" | "reference"`. `MemoryService` is the sole mutation boundary over `MemoryFileManager`: it owns CAS revisions, secret rejection, 8 KiB preferences, 16 KiB complete topic documents, the 200-topic cap, legacy shrink-only edits, index rebuilds, and deterministic receipt replay. Existing `memory_write` remains the immediate explicit-write path. Automatic learning is owned by the runtime-scoped `MemoryIdleCoordinator`: successful root Lead/Discussion conversations wait for 10 minutes of inactivity, then use at most one `fast` extraction call and one full-file reconciliation call; durable cursors, receipts, policy epochs, and warnings make restart and opt-out behavior explicit. Injection: ConfiguredAgent resolves one immutable Execution snapshot containing complete in-capacity preferences and project index; PromptContractCompiler labels it non-authoritative and emits its source/status in the durable Prompt trace.

## Session Goal System

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ Use an Automation when work should start once or on a recurring schedule. Goals
and Automations are optional; both remain visible through Sessions inside the
same workbench.

## Durable, inspectable memory

ArchCode keeps personal preferences and project knowledge as ordinary Markdown.
Explicit requests to remember something are saved immediately through the
Memory tool. Other durable context is considered only after a successful root
conversation has been idle for 10 minutes, then reconciled against the complete
Memory files it actually affects. Settings → Memory lets you inspect, edit,
delete, disable recall, or opt out of automatic learning without deleting data.

## Run it your way

| Where ArchCode runs | Good for | How you open it |
Expand Down
4 changes: 4 additions & 0 deletions apps/server/src/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ describe("createRuntimeApp", () => {
subscribeSessionEvents: mock((listener: (event: GlobalSSEEvent) => void) => {
listener({ type: "event", slug: "proj", sessionId: "session-1", eventId: 1, createdAt: 1, agentName: "lead", payload: {
type: "execution-start",
memoryPolicy: {
policy: { useMemory: true, autoLearning: true },
epoch: { bootId: "test-memory-boot", generation: 0 },
},
executionId: "run-1",
binding: {
selection: { model: "local:test" },
Expand Down
3 changes: 3 additions & 0 deletions apps/server/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { createAutomationsRoutes } from "./routes/automations";
import { createAttachmentsRoutes } from "./routes/attachments";
import { createMessagesRoutes } from "./routes/messages";
import { createMcpRoutes } from "./routes/mcp";
import { createMemoryRoutes } from "./routes/memory";
import { createProjectsRoutes } from "./routes/projects";
import { createSessionsRoutes } from "./routes/sessions";
import { createTodosRoutes } from "./routes/todos";
Expand Down Expand Up @@ -82,6 +83,7 @@ export function createRuntimeApp(
const files = createFilesRoutes(serverRuntime);
const directories = createDirectoriesRoutes();
const mcp = createMcpRoutes(serverRuntime);
const memory = createMemoryRoutes(serverRuntime);

app.route("/api", globalWork);
app.route("/api/projects", projects);
Expand All @@ -95,6 +97,7 @@ export function createRuntimeApp(
app.route("/api/projects/:slug/sessions/:sessionId/tool-outputs", toolOutputs);
app.route("/api/events", globalEvents);
app.route("/api/projects", files);
app.route("/api/projects", memory);
app.route("/api/mcp", mcp);
app.route("/api/sessions", new Hono());
app.route("/api/agents", agents);
Expand Down
6 changes: 6 additions & 0 deletions apps/server/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ export type ServerErrorCode =
| "CONFIG_REVISION_CONFLICT"
| "CONFIG_VALIDATION_ERROR"
| "CONFIG_RECOVERY_CONFLICT"
| "MEMORY_NOT_FOUND"
| "MEMORY_REVISION_CONFLICT"
| "MEMORY_CAPACITY_EXCEEDED"
| "MEMORY_INVALID_INPUT"
| "MEMORY_SECRET_DETECTED"
| "MEMORY_OPERATION_FAILED"
| "TOOL_OUTPUT_FORBIDDEN"
| "TOOL_OUTPUT_NOT_FOUND"
| "TOOL_OUTPUT_EXPIRED"
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/config-recovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ function safeIssuePath(path: string, configPath: string): string {
}
if (segments[0] === "memory") {
const field = segments[1];
return new Set(["enabled", "minMessages", "minContentLength", "cooldownMs"]).has(field ?? "")
return new Set(["useMemory", "autoLearning"]).has(field ?? "")
? `memory.${field}`
: "memory.<field>";
}
Expand Down
Loading