[ { "observations": [ { "title": "Argument checks and the constructor fix the execution boundary", "text": "validate_arguments covers only the JSON Schema subset used in this tutorial. The constructor keeps the model, tool table, and Step limit, so the loop does not need to rediscover those dependencies on every request.", "lines": [27, 45] }, { "title": "One Step fixes a request and decides whether to stop", "text": "run first writes the user objective, then combines system rules, Tool Schema, current history, and the Step number into a request. When the model has no tool_calls, the current Turn ends.", "lines": [47, 65] }, { "title": "Tool execution and failures both return to history", "text": "Unknown tools, invalid arguments, and execution exceptions all become tool results in messages. When max_steps is reached, the Harness raises an explicit error.", "lines": [67, 79] } ], "fills": [ { "label": "Model protocol, Tool structure, and Agent shape" }, { "label": "Argument checks and the Agent's fixed dependencies" }, { "label": "run: build a request, receive a reply, and decide whether to stop" }, { "label": "Tool execution: validation, exceptions, and feedback" } ], "changeStory": { "title": "Establish a verifiable Python execution baseline", "summary": "Python Nano DSH uses Agent.run() to advance several Steps within one Turn: the model returns a Tool Call, the runtime checks its arguments against a simplified Schema and executes the tool, then writes the result into the next request. It retains the limitation that tools, history, and records remain in memory for later chapters to address.", "harnessRole": "Execution baseline: convert model actions into validated tool results", "connection": "This chapter first closes the loop between model, tool, and feedback. Chapter 2 controls the request view, Chapter 3 manages installable capabilities, and Chapter 4 puts runtime facts into a common record.", "outcomes": [ "Trace requests, Tool Calls, Tool Results, and the next Step through Agent.run()", "Explain why the runtime still needs to find tools, check arguments, and wrap exceptions after the model proposes an action", "Distinguish a Step, a Turn, and max_steps, and identify the runtime problems this baseline leaves open" ] } }, { "observations": [ { "title": "Projection preserves the original messages", "text": "Short results return in full. Long results retain their beginning and end and report the amount omitted. The function builds a model view while leaving the original content available to the caller.", "lines": [9, 18] }, { "title": "Content that changes less often comes first", "text": "build_request places system rules and tools first, followed by projected history. The dynamicContext that changes in each Step comes last.", "lines": [21, 38] }, { "title": "Prefix comparison provides a teaching estimate", "text": "Character counts provide an approximate token count. shared_prefix compares four top-level components in a fixed order until the first difference; it does not query the provider's actual cache.", "lines": [41, 51] } ], "fills": [ { "label": "Projection and request-comparison function shapes" }, { "label": "project_tool_result: clip long results in the model view" }, { "label": "build_request: order stable content, history, and Step instructions" }, { "label": "Estimate text size and locate the first change" } ], "changeStory": { "title": "Complete facts and the current request have different jobs", "summary": "Python Nano DSH copies messages while building a request and clips only the model view of Tool Results. System rules and tools come first, history appends in order, and Step instructions come last; shared_prefix gives a deterministic teaching comparison of four top-level components.", "harnessRole": "Input projection layer: determine what the current Step sends to the model", "connection": "Chapter 1 needs a request, and this chapter defines how to create one from complete history. Chapter 3 changes the available tools; Chapter 4 provides a fuller record structure for complete facts.", "outcomes": [ "Distinguish original messages from the projected messages sent to the model", "Explain why project_tool_result() changes only the current request view", "Use the order of system, tools, messages, and dynamicContext to identify the first changed component" ] } }, { "observations": [ { "title": "Context keeps owners and effects", "text": "The runtime registry keeps Contributions. The plugin currently being installed determines the owner, and each plugin has its own list of cleanup functions.", "lines": [25, 38] }, { "title": "A successful mount takes effect; a failed one rolls back", "text": "When setup raises an error, Context removes registered content in reverse order. After setup succeeds, mount returns an idempotent unmount function. Explicit removal uses the same cleanup functions.", "lines": [39, 65] }, { "title": "Registration also records a cleanup function", "text": "Tools and Prompts enter the registry only during installation. Each registration immediately adds a cleanup function that removes the owner's contribution, and inspect generates a view of the current runtime.", "lines": [67, 89] } ], "fills": [ { "label": "Plugin protocol, contribution records, and Context shape" }, { "label": "Mount state, the effect stack, and reversible mounting" }, { "label": "Register Tools and Prompts while binding cleanup actions" }, { "label": "Inspect current capabilities and protect the installation boundary" } ], "changeStory": { "title": "The Python version keeps capability ownership and a reversible lifecycle", "summary": "Full DSH uses a Cordis plugin tree to organize models, tools, sessions, and the Agent Loop. The Python teaching version reduces that idea to owners and effects: it directly registers Tool and Prompt contributions, then cleans them in reverse order after a failed install or intentional unload. Its services and listeners containers reserve space for extension; the sample has no Service injection or dependency graph.", "harnessRole": "Runtime assembly layer: maintain the source and lifecycle of current tools and prompts", "connection": "Chapter 1 consumes a tool table, and Chapter 2 places it into a request. This chapter explains how capabilities enter and leave Context. Chapter 4 records runtime facts through messages and events.", "outcomes": [ "Distinguish a Tool, an action available to the model, from a Plugin, a unit of installed capability", "Trace installation, failed rollback, and unloading through Context.mount(), effect(), and the returned unmount function", "Use inspect() owner records to explain where current Tools and Prompts came from" ] } }, { "observations": [ { "title": "Events append with increasing identifiers", "text": "append uses the current length to assign a stable ID. request_step_ids reads header events to identify model Steps that can be reconstructed.", "lines": [16, 26] }, { "title": "The request header defines the reconstruction range", "text": "step_id locates the target header first. Content before that event forms the current Step's history, and the latest checkpoint determines where reconstruction resumes.", "lines": [28, 54] }, { "title": "Requests and Traces use the same log", "text": "Message projection reads recognized events, while trace formats every Event. Both views are generated from the Session Log.", "lines": [56, 68] } ], "fills": [ { "label": "Event structure and the basic SessionLog shape" }, { "label": "Append facts and index reconstructable Steps" }, { "label": "Rebuild the current request from a header and checkpoint" }, { "label": "Restore messages from events" }, { "label": "Generate a Trace from the same event stream" } ], "changeStory": { "title": "The Python version derives requests and a timeline from one log", "summary": "Python Nano DSH appends Events to SessionLog, then uses a request/header, checkpoints, and prior events to rebuild model messages. trace() creates a display timeline from the same sequence. The minimum version asks its caller to append events explicitly and does not automatically attach the log to the plugin runtime or persistence.", "harnessRole": "Record layer: supply common input for request reconstruction and execution views", "connection": "Chapter 1 produces model and tool round-trips, Chapter 2 explains the request view, and Chapter 3 manages capability lifecycles. This chapter establishes a simple sequence of recordable facts that full DSH can extend in the final chapters.", "outcomes": [ "Explain how increasing event identifiers preserve occurrence order", "Explain how request/header and context/checkpoint establish a reconstruction range", "Distinguish SessionLog, the request from build_request(), and the timeline from trace()" ] } }, { "observations": [ { "title": "The definition table stores code and runtime state", "text": "setup creates a definition table and increasing identifiers, then registers an effect that clears them during removal. cordis_inspect returns the current runtime view.", "lines": [29, 40] }, { "title": "Definition and execution are separate actions", "text": "cordis_define compiles and stores Python plugin code submitted by the Agent. cordis_run retrieves the code by plugin_id, executes it to obtain a Plugin, and mounts it through context.mount().", "lines": [42, 71] }, { "title": "Stopping and deletion share the unload function", "text": "cordis_stop calls the unload function while retaining the definition. cordis_undefine stops the plugin and deletes its definition. Context removes its contributions as part of the same lifecycle.", "lines": [73, 94] } ], "fills": [ { "label": "Tool wrapper and Runtime Tools plugin shape" }, { "label": "Definition table, cleanup actions, and inspection interface" }, { "label": "Define and mount a dynamic Python plugin" }, { "label": "Stop, delete, register tools, and load plugin code" } ], "changeStory": { "title": "Inspect, mount, validate, and release a capability gap", "summary": "Python Nano DSH exposes cordis_inspect, cordis_define, cordis_run, cordis_stop, and cordis_undefine as ordinary tools. The caller confirms a gap, registers and mounts a Python plugin, retrieves its new tool from Context to validate the result, then stops or deletes the definition. Dynamic code loads through built-in compile() and exec() for trusted teaching samples only.", "harnessRole": "Capability evolution layer: add and release runtime capability during a task", "connection": "This path uses the earlier chapters: Runtime Tools expose capability changes and Context manages mounting and unloading. A caller must feed context.tools back into an Agent before later requests can see a new tool; this minimum version also does not automatically add definition and mounting events to SessionLog.", "outcomes": [ "Describe a capability change in the order inspect, define, mount, validate, and release", "Explain the difference among code being defined, a plugin being running, and a new tool being verified", "Identify why dynamic code execution requires a trusted source and explicit security boundary" ] } }, { "observations": [ { "title": "Goal keeps cross-Round state in one place", "text": "The objective, status, number of started Rounds, and Round limit live in one object. Each Round's stage description is defined in advance outside the loop.", "lines": [13, 26] }, { "title": "Every Round restates the objective and current stage", "text": "run_long_task continues only while the Goal is active and below its limit. Every call to run_round receives the same objective and the current stage description.", "lines": [29, 33] }, { "title": "Status determines whether execution continues", "text": "accepted and blocked end the Goal. A Goal still active at the Round boundary becomes limit_reached.", "lines": [34, 41] } ], "fills": [ { "label": "Goal data, callback type, and runner shape" }, { "label": "Predefine each Round's task instruction" }, { "label": "Advance a Round and call run_round" }, { "label": "Finish or continue the Goal from result text" } ], "changeStory": { "title": "The Python version uses a Goal to continue past an ordinary Turn", "summary": "Python Nano DSH keeps the objective, status, and Round count in Goal. run_long_task() passes the objective and stage instruction to an external run_round for each Round, then updates status from accepted or blocked in the returned text. A Goal still active at the limit becomes limit_reached.", "harnessRole": "Long-task coordination layer: let bounded Rounds advance one objective together", "connection": "Each run_round can continue to call the Chapter 1 Agent and reuse state retained by an outer layer. Compared with the TypeScript version, this teaching implementation has no structured RoundResult, progress check, or automatic SessionLog events.", "outcomes": [ "Distinguish the four control layers: Goal, Round, Turn, and Step", "Explain how run_long_task() uses status and a Round count to start another attempt", "Identify accepted, blocked, and limit_reached as exits, and the limitation of text matching" ] } } ]