@orboto/mcp 0.213.0 → 0.213.1

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.
@@ -239,7 +239,7 @@ export const TruncationBlockSchema = z.object({
239
239
  */
240
240
  export const TruncationBlockAdvertisedSchema = z.object({ handle: z.string() })
241
241
  .passthrough()
242
- .describe('Response was cut; pass `handle` to orboto_response_expand.');
242
+ .describe('Response cut; pass `handle` to orboto_response_expand.');
243
243
  function stringMarker(omitted, handle, path) {
244
244
  return `... [truncated ${omitted} chars - full value: orboto_response_expand handle="${handle}" path="${path}"]`;
245
245
  }
@@ -10,7 +10,7 @@ import { mcpInstanceToken } from './shared.js';
10
10
  import { AgentSessionScopeSchema } from './agent-session-scope.js';
11
11
  export const agentHeartbeatToolConfig = {
12
12
  title: 'Agent heartbeat (Multi-Agent Coordination)',
13
- description: 'Register or refresh this agent\'s presence with status detail. A live connection already counts as online (an open MCP event stream, `orboto messages --follow`, the agent WebSocket) - the heartbeat adds status: idle (default) | working (+workingOnTicketId) | blocked, capabilities (free-form strings for operator filters) and clientInfo.name (the runtime), and is the only presence path for turn-based clients without a standing connection. Rows older than 90 s count as offline; persist the returned sessionToken and send it on later heartbeats. `scope` { projectKeys, ticketKeys, role } declares this session\'s responsibility (ORB-2136); an empty object clears it.',
13
+ description: 'Register or refresh this agent\'s presence with status detail. A live connection already counts as online (an open MCP event stream, `orboto messages --follow`, the agent WebSocket) - the heartbeat adds status: idle (default) | working (+workingOnTicketId) | blocked, capabilities (free-form strings for operator filters) and clientInfo.name (the runtime), and is the only presence path for turn-based clients without a standing connection. Rows older than 90 s count as offline; persist the returned sessionToken and send it on later heartbeats. `scope` { projectKeys, ticketKeys, role } declares this session\'s responsibility (ORB-2136); an empty object clears it. `startedBy`: starter session id, same account.',
14
14
  inputSchema: z.object({
15
15
  sessionToken: z.string().nullable().optional(),
16
16
  scope: AgentSessionScopeSchema.optional(),
@@ -23,11 +23,13 @@ export const agentHeartbeatToolConfig = {
23
23
  host: z.string().optional(),
24
24
  user_agent: z.string().optional(),
25
25
  }).optional(),
26
+ startedBy: z.string().uuid().optional(),
26
27
  }).shape,
27
28
  outputSchema: z.object({
28
29
  sessionToken: z.string(),
29
30
  sessionId: z.string().uuid(),
30
31
  scope: AgentSessionScopeSchema.nullable(),
32
+ startedBy: z.string().uuid().nullable().optional(),
31
33
  }).shape,
32
34
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },
33
35
  };
@@ -37,7 +39,7 @@ export function makeAgentHeartbeatHandler(client) {
37
39
  const scope = res.scope ? ` scope=${JSON.stringify(res.scope)}` : '';
38
40
  return {
39
41
  content: [{ type: 'text', text: `heartbeat ack - sessionToken=${res.sessionToken.slice(0, 8)}… session ${res.sessionId.slice(0, 8)}${scope}` }],
40
- structuredContent: { sessionToken: res.sessionToken, sessionId: res.sessionId, scope: res.scope ?? null },
42
+ structuredContent: { sessionToken: res.sessionToken, sessionId: res.sessionId, scope: res.scope ?? null, ...(res.startedBy !== undefined ? { startedBy: res.startedBy } : {}) },
41
43
  };
42
44
  };
43
45
  }
@@ -266,6 +266,17 @@ describe('ORB-2136 - session addressing', () => {
266
266
  expect(seen[0].headers['x-orboto-agent-session']).toBe('mcp-abc123');
267
267
  expect(result.structuredContent).toMatchObject({ scope: { role: 'integrator', projectKeys: ['ORB'] } });
268
268
  });
269
+ it('agent_heartbeat names the starter and answers the recorded one (ORB-2449)', async () => {
270
+ const starter = '5a1c0d2e-7b3f-4c11-9e2a-0c0ffee00001';
271
+ const seen = [];
272
+ vi.spyOn(globalThis, 'fetch').mockImplementation(async (_url, init) => {
273
+ seen.push(JSON.parse(init?.body ?? '{}'));
274
+ return { ok: true, status: 200, statusText: 'OK', json: async () => ({ sessionToken: 'mcp-abc123', sessionId: 'cbb52195-0000-4000-8000-000000000000', scope: { role: 'worker' }, startedBy: starter }), text: async () => '' };
275
+ });
276
+ const result = await makeAgentHeartbeatHandler(client)({ scope: { role: 'worker' }, startedBy: starter }, { sessionId: 'abc123' });
277
+ expect(seen[0]).toMatchObject({ startedBy: starter });
278
+ expect(result.structuredContent).toMatchObject({ startedBy: starter });
279
+ });
269
280
  });
270
281
  describe('orboto_agent_notify role addresses (ORB-2264)', () => {
271
282
  it('sends `to` as written, lists every recipient and needs no targetEmail', async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orboto/mcp",
3
- "version": "0.213.0",
3
+ "version": "0.213.1",
4
4
  "description": "Model Context Protocol server for orboto - exposes tickets, projects, milestones, time, docs, primer-facts, alerts and absences as MCP tools for Claude Desktop / Cursor / Copilot Chat / any MCP-aware AI client.",
5
5
  "license": "MIT",
6
6
  "type": "module",