feat(mcp): books.start v2 and the agent's self-report (P5)
Agent reason: the first call of a session should say who is asking, what is
waiting and what to do, in under 8 KiB, from the same lists the viewer
draws. books.start now carries book.id and book.url (the book's path on the
server, never a path on a disk), you {principal, kind, scopes, client_id},
drafts {building, awaiting_evidence}, reconciliation {tracked,
never_reconciled, behind}, backup {last_at, through_seq, stale}, the
prompts, and needs_attention (the twelve most urgent items, problems first,
with every item counted by kind), beside next_steps now in the envelope's
shape (tool or prompt, why, required; attention problems are required). A
new book is told to run the onboard prompt first, then to record the
company's facts and set up the chart; an active one is offered a draft
entry. The tool carries _meta anthropic/alwaysLoad, so a client that defers
tools loads it up front. The size fit shortens attention items and waiting
drafts after the convention summaries and before the lists, as before.
agent {model, reasoning_effort, harness} is accepted and written to the
call's access record (the access log shows it as agent), self-reported and
never checked, and used for nothing else: not in the answer, not in any
decision. Who asked is the request's alone: it is cleared when the request
ends, so a later resource read on the same thread cannot see it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
1 parent
996c5f40f4
commit
453abb62ea
7 files changed
+459
-25
No files matched your search
@@ -67,6 +67,22 @@ pub struct Client {
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
/// What an agent said about itself when it began its session
|
||||
/// (`books.start`'s `agent`): self-reported, never checked, kept beside the
|
||||
/// access so a record shows which model and harness made it.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Agent {
|
||||
/// The model's name.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub model: Option<String>,
|
||||
/// The reasoning effort it ran at.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub reasoning_effort: Option<String>,
|
||||
/// The program driving it.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub harness: Option<String>,
|
||||
}
|
||||
|
||||
/// What came of a call.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -105,6 +121,9 @@ pub struct Access {
|
||||
/// (whatever it answered).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub head_after: Option<Head>,
|
||||
/// What the agent said about itself, on a `books.start` that said.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub agent: Option<Agent>,
|
||||
}
|
||||
|
||||
/// Mask any full TIN in a client's text, then cut it to
|
||||
@@ -186,6 +205,11 @@ impl AccessLog {
|
||||
c.name = asserted(&c.name);
|
||||
c.version = asserted(&c.version);
|
||||
}
|
||||
if let Some(a) = &mut access.agent {
|
||||
for f in [&mut a.model, &mut a.reasoning_effort, &mut a.harness] {
|
||||
*f = f.as_deref().map(asserted);
|
||||
}
|
||||
}
|
||||
access.request_id = access.request_id.as_deref().map(asserted);
|
||||
access.subject = asserted(&access.subject);
|
||||
let Value::Object(fields) = serde_json::to_value(&access).expect("a record serializes")
|
||||
@@ -261,6 +285,7 @@ mod tests {
|
||||
args_digest: Some(digest(&serde_json::json!({"as_of": "2026-10-19"}))),
|
||||
result: Outcome::Ok,
|
||||
head_after: None,
|
||||
agent: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user