bantamkit-mcp 0.35.2 → 0.35.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shiftwork_clock_out",
3
- "description": "Shift-work clock-out: record a finished unit — set its status, advance plan.cursor, merge handoff_patch, push history_entry onto the 5-entry ring — validating the whole mutated document against the checkpoint schema BEFORE an atomic write (a failure writes nothing and returns result=error). Every success appends one accounting line (unit, role, status, ts, briefed, plus your accounting fields, e.g. tokens/duration_ms/model) to <checkpoint>.log.jsonl, beside the brief lines clock_in writes (a brief line carries `event` and no `status`). `briefed` is written by the runtime, never taken from you — a self-reported value is overwritten by the measured one: true when a brief was issued for this unit since its LAST clock-out (not ever), so a unit re-run inline after a blocked clock-out reads false. It records; it never refuses — a unit that was never clocked in still clocks out, with briefed=false. If the checkpoint's job.roles names this unit's role, accounting.model must be one of that role's model identifiers, spelled exactly \u2014 a model that is not on the list, or none reported at all, is refused before anything is written; a role job.roles omits, and a checkpoint carrying no job.roles, are unconstrained.",
3
+ "description": "Shift-work clock-out: record a finished unit — set its status, advance plan.cursor, merge handoff_patch, push history_entry onto the 5-entry ring — validating the whole mutated document against the checkpoint schema BEFORE an atomic write (a failure writes nothing and returns result=error). `unit_id` must be `plan.cursor`, the one unit clock_in briefs; any other id is refused (`unit X is not the cursor unit Y`). `handoff_patch` and `history_entry` are both required arguments: `handoff_patch` is shallow-merged into `handoff` and takes only its four keys (pass `{}` to leave it unchanged); `history_entry` needs `unit` and `outcome`; `status` is one of the five unit statuses — a value outside these shapes is refused by the schema check, nothing written. Every success appends one accounting line (unit, role, status, ts, briefed, plus your accounting fields, e.g. tokens/duration_ms/model) to <checkpoint>.log.jsonl, beside the brief lines clock_in writes (a brief line carries `event` and no `status`). `accounting` is optional: null, the default, logs a line with no cost figures at all; a non-null line must fit the accounting shape below. `briefed` is written by the runtime, never taken from you — a self-reported value is overwritten by the measured one: true when a brief was issued for this unit since its LAST clock-out (not ever), so a unit re-run inline after a blocked clock-out reads false. A brief is not a precondition: a unit that was never clocked in still clocks out, with briefed=false. If the checkpoint's job.roles names this unit's role, accounting.model must be one of that role's model identifiers, spelled exactly — a model that is not on the list, or none reported at all, is refused before anything is written; a role job.roles omits, and a checkpoint carrying no job.roles, are unconstrained.",
4
4
  "surfaces": [
5
5
  "mcp"
6
6
  ],
@@ -12,21 +12,76 @@
12
12
  },
13
13
  "unit_id": {
14
14
  "title": "Unit Id",
15
- "type": "string"
15
+ "type": "string",
16
+ "description": "The unit being clocked out. Must equal plan.cursor — the single-pointer contract: clock_in briefs only the cursor unit and clock_out accepts only it."
16
17
  },
17
18
  "status": {
18
19
  "title": "Status",
19
- "type": "string"
20
+ "type": "string",
21
+ "enum": [
22
+ "todo",
23
+ "in_progress",
24
+ "done",
25
+ "blocked",
26
+ "dropped"
27
+ ],
28
+ "description": "The unit's new status; the same five values the checkpoint schema allows on plan.units[].status."
20
29
  },
21
30
  "handoff_patch": {
22
- "additionalProperties": true,
23
31
  "title": "Handoff Patch",
24
- "type": "object"
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "description": "Shallow-merged into `handoff`: only these four keys exist there, and a key outside them is refused by the schema check. Required; `{}` leaves `handoff` unchanged.",
35
+ "properties": {
36
+ "next_action": {
37
+ "type": "string",
38
+ "minLength": 1,
39
+ "description": "Imperative first move — zero re-derivation."
40
+ },
41
+ "open_questions": {
42
+ "type": "array",
43
+ "items": {
44
+ "type": "string",
45
+ "minLength": 1
46
+ },
47
+ "description": "The autonomy switch: empty = driver keeps cycling, non-empty = STOP and ask."
48
+ },
49
+ "do_not": {
50
+ "type": "array",
51
+ "items": {
52
+ "type": "string",
53
+ "minLength": 1
54
+ },
55
+ "description": "Negative space — near-mistakes past sessions made."
56
+ },
57
+ "notes": {
58
+ "type": "string",
59
+ "description": "Free-form prose for the next session, one string; the empty string clears it."
60
+ }
61
+ }
25
62
  },
26
63
  "history_entry": {
27
- "additionalProperties": true,
28
64
  "title": "History Entry",
29
- "type": "object"
65
+ "type": "object",
66
+ "required": [
67
+ "unit",
68
+ "outcome"
69
+ ],
70
+ "additionalProperties": true,
71
+ "description": "Pushed onto the 5-entry `history` ring; `unit` and `outcome` are required, `notes` optional, other keys pass through.",
72
+ "properties": {
73
+ "unit": {
74
+ "type": "string",
75
+ "minLength": 1
76
+ },
77
+ "outcome": {
78
+ "type": "string",
79
+ "minLength": 1
80
+ },
81
+ "notes": {
82
+ "type": "string"
83
+ }
84
+ }
30
85
  },
31
86
  "accounting": {
32
87
  "anyOf": [
@@ -37,7 +92,7 @@
37
92
  "tokens": {
38
93
  "type": "integer",
39
94
  "minimum": 0,
40
- "description": "Tokens the unit consumed as the harness's subagent counter reports them: every class it reports (input, output, cache creation) summed, EXCLUDING cache reads. Required, because a unit whose cost is unknown cannot be compared with any other; a rounded self-estimate is allowed only if `note` says it is one."
95
+ "description": "Tokens the unit consumed as the harness's subagent counter reports them: every class it reports (input, output, cache creation) summed, EXCLUDING cache reads. In a non-null accounting line it is required — a line without it is refused before anything is written; only `accounting: null`, the default, is exempt, and that logs no cost at all. A rounded self-estimate is allowed only if `note` says it is one; a unit whose cost is unknown cannot be compared with any other."
41
96
  },
42
97
  "cache_read_tokens": {
43
98
  "type": "integer",
@@ -47,7 +102,7 @@
47
102
  "duration_ms": {
48
103
  "type": "integer",
49
104
  "minimum": 0,
50
- "description": "Wall-clock from spawning the subagent to its final message, in whole milliseconds. Required, and the only duration key with a defined meaning: older lines carry `duration`, `duration_min` or `duration_s`, which this schema neither reads nor renames."
105
+ "description": "Wall-clock from spawning the subagent to its final message, in whole milliseconds. In a non-null line it is required, and it is the only duration key with a defined meaning: older lines carry `duration`, `duration_min` or `duration_s`, which this schema neither reads nor renames."
51
106
  },
52
107
  "model": {
53
108
  "type": "string",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shiftwork_plan",
3
- "description": "Shift-work plan: read-only batch view of a checkpoint — which units its `depends_on` graph permits to run in parallel. Answers `{\"result\": \"plan\", \"batches\", \"ready\", \"sequence\", \"width\", \"cursor\"}`: `ready` is `batches[0]`, the units dispatchable right now, and `width` is the largest batch length, the widest fan-out. Units with status `done` or `dropped` are satisfied — dropped from the graph, and edges pointing at them treated as already resolved; `todo`, `in_progress` and `blocked` stay in it. Every unit has priority 0, because the checkpoint schema has no priority field, so order inside a batch is `plan.units` order. Same refusal sentences as `work_plan` for a duplicate id, an unknown dependency or a cycle, and the same refusals `shiftwork_status` gives for a checkpoint it cannot read. It reports what the dependency graph permits; it does not move the cursor, which is echoed unchanged so the single-pointer contract and the batch view can be read side by side. An orchestrator stays responsible for what it actually dispatches: `depends_on` encodes logical order, not file contention. Never mutates.",
3
+ "description": "Shift-work plan: read-only batch view of a checkpoint — which units its `depends_on` graph permits to run in parallel. Answers `{\"result\": \"plan\", \"batches\", \"ready\", \"sequence\", \"width\", \"cursor\"}`: `ready` is `batches[0]`, the units whose dependencies are all satisfied, and `width` is the largest batch length, the widest fan-out. Only `cursor` can be clocked: shiftwork_clock_in briefs it and shiftwork_clock_out refuses every other id, so a `ready` member beyond the cursor is what the graph permits, not a unit the clock accepts yet. Units with status `done` or `dropped` are satisfied — dropped from the graph, and edges pointing at them treated as already resolved; `todo`, `in_progress` and `blocked` stay in it. Every unit has priority 0, because the checkpoint schema has no priority field, so order inside a batch is `plan.units` order. Same refusal sentences as `work_plan` for a duplicate id, an unknown dependency or a cycle, and the same refusals `shiftwork_status` gives for a checkpoint it cannot read. It reports what the dependency graph permits; it does not move the cursor, which is echoed unchanged so the single-pointer contract and the batch view can be read side by side. An orchestrator stays responsible for what it actually dispatches: `depends_on` encodes logical order, not file contention. Never mutates.",
4
4
  "surfaces": [
5
5
  "mcp"
6
6
  ],
@@ -1068,6 +1068,24 @@ export function buildServer(memory, wire, version, log = EventLog.fromEnv(memory
1068
1068
  const uri = request.params.uri;
1069
1069
  const skill = /^bantamkit:\/\/skills\/([^/]*)$/.exec(uri);
1070
1070
  if (skill) {
1071
+ // A skill that pairs with a tool is served on that tool's surfaces and no other. The
1072
+ // pairing is the one the reference's `filegraph.py` spells — `load_tool("file_graph")`
1073
+ // beside `load_skill("file-graph")`, the skill's name with `-` for `_`. `file-graph` is
1074
+ // the eval agent's snippet telling it to call `file_graph`, whose asset claims only
1075
+ // `agent`; handing it to an MCP client names a tool `tools/list` does not carry (job60
1076
+ // row 46). Same sentence and code as the reference; the `instructions` suite gates it.
1077
+ const paired = skill[1].replace(/-/g, '_');
1078
+ let pairedSurfaces = null;
1079
+ try {
1080
+ pairedSurfaces = (loadToolAsset(paired)['surfaces'] ?? []);
1081
+ }
1082
+ catch (e) {
1083
+ if (!(e instanceof AssetNotFound))
1084
+ throw e;
1085
+ }
1086
+ if (pairedSurfaces !== null && !pairedSurfaces.includes('mcp')) {
1087
+ throw rpcError(ErrorCode.InternalError, `skill asset ${skill[1]} is not served here: it pairs with tool ${paired}, whose asset claims surfaces [${pairedSurfaces.map((s) => `'${s}'`).join(', ')}], not mcp`, { uri });
1088
+ }
1071
1089
  let text;
1072
1090
  try {
1073
1091
  text = loadSkill(skill[1]);
@@ -258,7 +258,7 @@ const FACTS_UNREADABLE = 'a store whose facts could not be listed is not a store
258
258
  "answering 'empty' here is what rewrites index.md from nothing";
259
259
  const ARCHIVE_UNREADABLE = 'an archive that could not be listed is not an empty archive, and answering ' +
260
260
  "'nothing is archived' here is what makes compaction look like deletion — the " +
261
- 'facts compact() moved are still on disk under this path';
261
+ 'facts that compaction moved are still on disk under this path';
262
262
  const ARCHIVE_UNREACHABLE = "an archived fact that could not be stat'd is not an archived fact that is not " +
263
263
  "there, and answering 'no archived fact' here sends the operator looking for a " +
264
264
  'file that is still on disk under this path';
@@ -1412,7 +1412,7 @@ export class MemoryStore {
1412
1412
  const size = Buffer.byteLength(this.indexText(), 'utf8');
1413
1413
  if (size > this.indexBudget) {
1414
1414
  throw new MemoryBudgetExceeded(`memory index is ${size} bytes, budget is ${this.indexBudget}: ` +
1415
- 'run compact() or tersen descriptions');
1415
+ 'compact the store or tersen descriptions');
1416
1416
  }
1417
1417
  }
1418
1418
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bantamkit-mcp",
3
- "version": "0.35.2",
3
+ "version": "0.35.3",
4
4
  "description": "Memory MCP server for Claude Code, Cursor, VS Code Copilot and Claude Desktop: per-person memory, JSON Schema validation, shift-work tools. Pure Node; install once with npx, run offline.",
5
5
  "keywords": [
6
6
  "mcp",