@orboto/mcp 0.200.0 → 0.200.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.
- package/dist/agent-head.js +51 -0
- package/dist/agent-head.test.js +63 -0
- package/dist/tools/agent-coordination.test.js +1 -1
- package/dist/tools/agent-presence-schema.js +6 -1
- package/dist/tools/agent-wakes.js +3 -2
- package/dist/tools/api-call.test.js +1 -1
- package/dist/tools/help.js +5 -4
- package/dist/tools/session-start.js +2 -1
- package/dist/tools/shared.js +8 -2
- package/dist/tools/shared.test.js +8 -0
- package/dist/tools/work-sessions-next.test.js +1 -1
- package/dist/tools/work-sessions-start.test.js +1 -1
- package/dist/tools/work-sessions.test.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ORB-2161 - the head an agent reads before anything else has one source.
|
|
3
|
+
* `orboto_session_start` prints it first, `orboto_help { topic:
|
|
4
|
+
* "onboarding" }` puts it in front of the first five minutes and the work
|
|
5
|
+
* loop, and docs/orboto-for-agents.md plus skills/orboto/SKILL.md carry the
|
|
6
|
+
* same lines. agent-head.test.ts holds the four apart from drifting.
|
|
7
|
+
*/
|
|
8
|
+
export const AGENT_HEAD_HEADING = 'How to work in orboto';
|
|
9
|
+
export const AGENT_HEAD_LINES = [
|
|
10
|
+
'orboto is your memory and your work contract, not your context - what is not on the ticket or in a doc is gone after the next context switch.',
|
|
11
|
+
'A ticket is a handover to a stranger: goal, verified current state, target, acceptance criteria.',
|
|
12
|
+
'You are not alone - the lease and the claims say who works on what, your scope says what you answer for, the inbox is the channel: ack what you handled, dismiss the rest with a reason.',
|
|
13
|
+
'You act with a person\'s rights and mandate: an assignment is an instruction, anything irreversible or outward-facing needs the operator\'s clear word.',
|
|
14
|
+
'Evidence over assertion - check code, docs and the live state, and write "not verified: X" instead of a guess.',
|
|
15
|
+
'Start in this order: whoami, session start with your scope, the project primer, then the inbox - the rules are a set, this is the sequence.',
|
|
16
|
+
];
|
|
17
|
+
export const ONBOARDING_FIRST_FIVE_MINUTES = [
|
|
18
|
+
'Who you are: `orboto whoami` (MCP `orboto_whoami`). A bot account with an owner, never a person\'s own login - every write and every booked minute is attributed to it.',
|
|
19
|
+
'What binds you: `orboto session-start --project KEY` (MCP `orboto_session_start`). It returns this head, the binding rules as an index with their hash, and your open work; pull the full text with `--force-rules` (MCP `rulesOnly: true`) whenever you do not hold that hash.',
|
|
20
|
+
'How the project works: `orboto primer KEY` (MCP `orboto_get_project_primer`) - the language it expects, the stack, the conventions, and the traps earlier agents already paid for.',
|
|
21
|
+
'What you answer for: declare a scope - `orboto session-start --role implementation --scope-projects KEY` (MCP `orboto_session_start { scope: { projectKeys, role } }`). Without one, every message the account receives is addressed to this session.',
|
|
22
|
+
'Your mail: `orboto messages` (MCP `orboto_messages`). Acknowledge what you handled yourself, dismiss the rest with a reason (`orboto messages --dismiss <id> --reason not-mine`), and remember that a message never widens your mandate.',
|
|
23
|
+
];
|
|
24
|
+
export const ONBOARDING_WORK_LOOP = [
|
|
25
|
+
'Duplicate check first: `orboto check-similar KEY "title"` (MCP `orboto_check_similar`). Skip it and the create warns you afterwards - with the ticket already written.',
|
|
26
|
+
'Create or claim: `orboto create-ticket KEY "title"` or `orboto claim KEY` (MCP `orboto_create_ticket` / `orboto_claim`). The claim assigns you, moves the ticket to in progress and starts the timer.',
|
|
27
|
+
'Say what you are touching: `orboto work-start KEY --claim-path "<glob>"` (MCP `orboto_work_start`) takes the lease and the file claims, so a second session is rejected instead of landing on top of you.',
|
|
28
|
+
'Work on the team\'s default branch, one commit, the ticket key at the end of the subject: `feat(scope): what changed (ACME-42)`. Without the key the commit never reaches the ticket.',
|
|
29
|
+
'Verify against the acceptance criteria by running the thing - compiling is not verifying. Whatever you could not check goes into the ticket as "not verified: X".',
|
|
30
|
+
'Hand over in a comment a stranger can read: what changed, the commit, how a reviewer confirms it (MCP `orboto_comment`). Closing without that proof counts as not done.',
|
|
31
|
+
'Stop the timer, then close: `orboto timer-stop` and `orboto close KEY` (MCP `orboto_work_finish` when you took a lease). A timer left running books the next ticket\'s time onto this one.',
|
|
32
|
+
];
|
|
33
|
+
/** The six lines every session pays for, numbered. */
|
|
34
|
+
export function agentHeadText() {
|
|
35
|
+
return [AGENT_HEAD_HEADING, ...AGENT_HEAD_LINES.map((line, index) => `${index + 1}. ${line}`)].join('\n');
|
|
36
|
+
}
|
|
37
|
+
/** The `onboarding` help topic: the head, the first five minutes, the work loop. */
|
|
38
|
+
export function onboardingText() {
|
|
39
|
+
return [
|
|
40
|
+
agentHeadText(),
|
|
41
|
+
'',
|
|
42
|
+
'The first five minutes, in this order',
|
|
43
|
+
...ONBOARDING_FIRST_FIVE_MINUTES.map((step, index) => `${index + 1}. ${step}`),
|
|
44
|
+
'',
|
|
45
|
+
'The work loop, always the same',
|
|
46
|
+
...ONBOARDING_WORK_LOOP.map((step, index) => `${index + 1}. ${step}`),
|
|
47
|
+
'',
|
|
48
|
+
'The whole page - the object map, several sessions on one account, the autonomy boundary,',
|
|
49
|
+
'evidence over assertion, and the traps that cost other agents a run: docs/orboto-for-agents.md.',
|
|
50
|
+
].join('\n');
|
|
51
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ORB-2161 - the head has one source. Session start prints it before
|
|
3
|
+
* anything else, the onboarding help topic repeats it, and the page and
|
|
4
|
+
* the skill carry the same lines, so a sentence cannot be improved in one
|
|
5
|
+
* place and stay old in the other three.
|
|
6
|
+
*/
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
import { AGENT_HEAD_HEADING, AGENT_HEAD_LINES, ONBOARDING_FIRST_FIVE_MINUTES, ONBOARDING_WORK_LOOP, agentHeadText, onboardingText } from './agent-head.js';
|
|
10
|
+
import { makeHelpHandler } from './tools/help.js';
|
|
11
|
+
import { makeSessionStartHandler } from './tools/session-start.js';
|
|
12
|
+
import { OrbotoClient } from './orboto-client.js';
|
|
13
|
+
const read = (relative) => readFileSync(new URL(`../../../${relative}`, import.meta.url), 'utf8');
|
|
14
|
+
const CARRIERS = ['docs/orboto-for-agents.md', 'skills/orboto/SKILL.md'];
|
|
15
|
+
const client = new OrbotoClient({ baseUrl: 'https://orboto.example.com', apiKey: 'orb_x' });
|
|
16
|
+
beforeEach(() => { vi.restoreAllMocks(); });
|
|
17
|
+
afterEach(() => { vi.restoreAllMocks(); });
|
|
18
|
+
describe('the agent head (ORB-2161)', () => {
|
|
19
|
+
it('stays four to six lines, so every session can afford it', () => {
|
|
20
|
+
expect(AGENT_HEAD_LINES.length).toBeGreaterThanOrEqual(4);
|
|
21
|
+
expect(AGENT_HEAD_LINES.length).toBeLessThanOrEqual(6);
|
|
22
|
+
for (const line of AGENT_HEAD_LINES)
|
|
23
|
+
expect(line).not.toContain('\n');
|
|
24
|
+
});
|
|
25
|
+
it.each(CARRIERS)('%s carries every line verbatim', (file) => {
|
|
26
|
+
const page = read(file);
|
|
27
|
+
for (const line of AGENT_HEAD_LINES)
|
|
28
|
+
expect(page).toContain(line);
|
|
29
|
+
});
|
|
30
|
+
it('is the first thing orboto_session_start prints', async () => {
|
|
31
|
+
vi.spyOn(globalThis, 'fetch').mockImplementation(async (url) => {
|
|
32
|
+
const path = new URL(url.toString()).pathname;
|
|
33
|
+
const body = path.startsWith('/agent-instructions')
|
|
34
|
+
? { instructions: 'claim -> commit -> close', rulesHash: 'fixture' }
|
|
35
|
+
: path.startsWith('/users/me/assigned-tickets') ? { items: [] } : {};
|
|
36
|
+
return { ok: true, status: 200, statusText: 'OK', json: async () => body, text: async () => '' };
|
|
37
|
+
});
|
|
38
|
+
const res = await makeSessionStartHandler(client)();
|
|
39
|
+
const text = res.content[0].text;
|
|
40
|
+
expect(text.startsWith(agentHeadText())).toBe(true);
|
|
41
|
+
expect(text.indexOf(AGENT_HEAD_HEADING)).toBeLessThan(text.indexOf('# orboto session start'));
|
|
42
|
+
});
|
|
43
|
+
it('orboto_help returns the head, the first five minutes and the work loop for the topic "onboarding"', async () => {
|
|
44
|
+
const result = await makeHelpHandler()({ topic: 'onboarding' });
|
|
45
|
+
const text = (result.content?.[0]).text;
|
|
46
|
+
for (const line of AGENT_HEAD_LINES)
|
|
47
|
+
expect(text).toContain(line);
|
|
48
|
+
for (const step of ONBOARDING_FIRST_FIVE_MINUTES)
|
|
49
|
+
expect(text).toContain(step);
|
|
50
|
+
for (const step of ONBOARDING_WORK_LOOP)
|
|
51
|
+
expect(text).toContain(step);
|
|
52
|
+
expect(text).toContain('docs/orboto-for-agents.md');
|
|
53
|
+
expect(result.structuredContent).toMatchObject({ tool: 'topic:onboarding' });
|
|
54
|
+
});
|
|
55
|
+
it('names both topics when an unknown one is asked for', async () => {
|
|
56
|
+
const unknown = (await makeHelpHandler()({ topic: 'nope' })).content?.[0];
|
|
57
|
+
expect(unknown.text).toContain('onboarding');
|
|
58
|
+
expect(unknown.text).toContain('connect');
|
|
59
|
+
});
|
|
60
|
+
it('the onboarding topic repeats the head instead of holding a second copy', () => {
|
|
61
|
+
expect(onboardingText().startsWith(agentHeadText())).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -149,7 +149,7 @@ describe('orboto_agent_notify', () => {
|
|
|
149
149
|
});
|
|
150
150
|
expect(result.content[0].text).toContain('notified bob@example.com');
|
|
151
151
|
expect(result.structuredContent).toMatchObject({ ok: true });
|
|
152
|
-
expect(capturedBody[0].senderRef).toMatch(/^
|
|
152
|
+
expect(capturedBody[0].senderRef).toMatch(/^agent-/);
|
|
153
153
|
});
|
|
154
154
|
it('ORB-1742: senderRef prefers the per-connection MCP session id, and an explicit ref wins outright', async () => {
|
|
155
155
|
const capturedBody = [];
|
|
@@ -34,11 +34,16 @@ export const AgentInventoryEntrySchema = z.object({
|
|
|
34
34
|
scope: AgentSessionScopeSchema.nullable(),
|
|
35
35
|
/** ORB-2136 - unread inbox messages this session would list. */
|
|
36
36
|
unreadMessages: z.number().int(),
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* ORB-2149 - the last 24 hours of wakes: how many deliveries, how precise,
|
|
39
|
+
* what the wrong ones cost. ORB-2170 - `backlogSeen` is the `nudge` count
|
|
40
|
+
* over the same window, a backlog sighting rather than a wake.
|
|
41
|
+
*/
|
|
38
42
|
wakes: z.object({
|
|
39
43
|
count: z.number().int(),
|
|
40
44
|
precision: z.number().nullable(),
|
|
41
45
|
wrongWakeContextTokens: z.number().int(),
|
|
46
|
+
backlogSeen: z.number().int(),
|
|
42
47
|
}),
|
|
43
48
|
lastSeenAt: z.string(),
|
|
44
49
|
createdAt: z.string(),
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
export const agentWakesToolConfig = {
|
|
7
7
|
title: 'Wake ledger',
|
|
8
|
-
description: 'Who woke this account\'s sessions, which visibility rule delivered the message (addressed | scope_project | scope_ticket | broadcast | unscoped_session | account_view), what the woken session did with it (handled | not_mine | obsolete | duplicate | expired | pending) and what the wake cost in context tokens. `stats: true` returns delivery precision per woken session and per sender over `hours` (operator read, needs admin:agents:read);
|
|
8
|
+
description: 'Who woke this account\'s sessions, which visibility rule delivered the message (addressed | scope_project | scope_ticket | broadcast | unscoped_session | account_view), what the woken session did with it (handled | not_mine | obsolete | duplicate | expired | pending) and what the wake cost in context tokens. `stats: true` returns delivery precision per woken session and per sender over `hours` (operator read, needs admin:agents:read); `wakes`, `precision` and the median latency count deliveries (`channel` | `follow` | `poll`) only, `backlogSeen` is the separate count of `nudge` rows - a backlog sighting, not a wake. Without `stats` it returns the ledger rows of your own account, newest first. Use it to prove a misrouting sender instead of describing it in prose.',
|
|
9
9
|
inputSchema: z.object({
|
|
10
10
|
stats: z.boolean().default(false).describe('Precision per session and per sender instead of rows.'),
|
|
11
11
|
hours: z.number().int().min(1).max(720).optional().describe('Stats window, default 24.'),
|
|
@@ -20,7 +20,8 @@ export const agentWakesToolConfig = {
|
|
|
20
20
|
function statsLine(e) {
|
|
21
21
|
const precision = e.precision === null ? 'unjudged' : `${Math.round(e.precision * 100)}%`;
|
|
22
22
|
const tokens = e.wrongWakeContextTokens > 0 ? `, wrong wakes cost ${e.wrongWakeContextTokens} tokens` : '';
|
|
23
|
-
|
|
23
|
+
const backlog = e.backlogSeen > 0 ? `, ${e.backlogSeen} backlog seen` : '';
|
|
24
|
+
return `- ${e.label}: ${e.wakes} wakes, precision ${precision}, median ${e.medianLatencyMs ?? '?'} ms, top rule ${e.topRule ?? '-'}${tokens}${backlog}`;
|
|
24
25
|
}
|
|
25
26
|
export function makeAgentWakesHandler(client) {
|
|
26
27
|
return async (args) => {
|
|
@@ -109,6 +109,6 @@ describe('orboto_api_call', () => {
|
|
|
109
109
|
await makeApiCallHandler(client)({ method: 'POST', path: '/v1/agent/heartbeat', body: { scope: { role: 'spec' } } }, { sessionId: 'abc123' });
|
|
110
110
|
expect(seen[0]['x-orboto-agent-session']).toBe('mcp-abc123');
|
|
111
111
|
await makeApiCallHandler(client)({ method: 'GET', path: '/v1/agent/messages' });
|
|
112
|
-
expect(seen[1]['x-orboto-agent-session']).toMatch(/^
|
|
112
|
+
expect(seen[1]['x-orboto-agent-session']).toMatch(/^agent-/);
|
|
113
113
|
});
|
|
114
114
|
});
|
package/dist/tools/help.js
CHANGED
|
@@ -10,14 +10,15 @@
|
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import { getToolDoc, listToolDocNames } from '../tool-docs.js';
|
|
12
12
|
import { connectScriptText } from '../connect-script.js';
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
import { onboardingText } from '../agent-head.js';
|
|
14
|
+
/** ORB-2157, ORB-2161 - guidance that belongs to no single tool, addressed by topic. */
|
|
15
|
+
const TOPICS = { connect: connectScriptText, onboarding: onboardingText };
|
|
15
16
|
export const helpToolConfig = {
|
|
16
17
|
title: 'Full guidance for one orboto tool',
|
|
17
|
-
description: 'Return the complete guidance text (workflows, warnings, edge cases) for one orboto tool by name - manifest descriptions are one-line summaries, this is the rest. Call it before first use of an unfamiliar write tool. topic: "connect" returns the step-by-step script for setting a person up
|
|
18
|
+
description: 'Return the complete guidance text (workflows, warnings, edge cases) for one orboto tool by name - manifest descriptions are one-line summaries, this is the rest. Call it before first use of an unfamiliar write tool. topic: "connect" returns the step-by-step script for setting a person up, topic: "onboarding" the first five minutes and the work loop.',
|
|
18
19
|
inputSchema: z.object({
|
|
19
20
|
tool: z.string().min(1).max(128).optional().describe('Tool name, e.g. orboto_create_ticket.'),
|
|
20
|
-
topic: z.enum(['connect']).optional().describe('Guidance topic instead of a tool.'),
|
|
21
|
+
topic: z.enum(['connect', 'onboarding']).optional().describe('Guidance topic instead of a tool.'),
|
|
21
22
|
}).shape,
|
|
22
23
|
outputSchema: z.object({
|
|
23
24
|
tool: z.string(),
|
|
@@ -15,6 +15,7 @@ import { PROTECT_TEXT_META, storePayload } from '../response-budget.js';
|
|
|
15
15
|
import { loadRequiredRules } from '../required-rules.js';
|
|
16
16
|
import { GIT_HEALTH_REASON_TEXT } from './git-health-reasons.js';
|
|
17
17
|
import { channelStartLines } from '../inbox-channel.js';
|
|
18
|
+
import { agentHeadText } from '../agent-head.js';
|
|
18
19
|
export const sessionStartToolConfig = {
|
|
19
20
|
title: 'Load the rules you must follow + re-orient',
|
|
20
21
|
description: 'THE canonical way to LOAD the binding workspace rules you must follow as an agent. Run it as your FIRST action in a session and immediately AFTER any context compaction. Returns a one-line-per-rule INDEX of the binding working-rules plus their hash (or a compact "unchanged" ack on a repeat call within the same connection) - read the index, then call this tool again with `rulesOnly: true` to read the full text of the rules whenever you do not already hold that exact hash, and expand before acting on any rule whose title touches what you are about to do. Also returns your in-progress tickets - each flagged LANDED, IDLE when it has a linked commit but has not moved for days, i.e. finished work you never handed to review - your running timer, and a warning if a project\'s git connection looks unhealthy (commit ingestion may be stalled). Pass `ticketKey` to also get a one-shot bundle for that ticket: project primer, the full ticket with dependencies + checklists, that project\'s git health, and any other agent sessions currently on it - replacing several separate calls. (Do NOT use orboto_list_agent_instructions to read the rules - that tool MANAGES/edits rule blocks for admins; this one is what you read to know how to work.) Registers this session (a heartbeat on its instance row) and is otherwise read-only. '
|
|
@@ -203,7 +204,7 @@ export function makeSessionStartHandler(client, opts = {}) {
|
|
|
203
204
|
? tickets.filter((t) => !t.projectId || t.projectId === scopeProjectId)
|
|
204
205
|
: tickets;
|
|
205
206
|
const elsewhereCount = tickets.length - scopedTickets.length;
|
|
206
|
-
const lines = ['# orboto session start'];
|
|
207
|
+
const lines = [agentHeadText(), '', '# orboto session start'];
|
|
207
208
|
if (me)
|
|
208
209
|
lines.push(`You are ${me.fullName ?? me.email}${me.email ? ` (${me.email})` : ''}.`);
|
|
209
210
|
if (me?.workspaceLocale || me?.locale)
|
package/dist/tools/shared.js
CHANGED
|
@@ -95,8 +95,14 @@ export function ticketLine(t) {
|
|
|
95
95
|
parts.push('[waiting on Git ingestion]');
|
|
96
96
|
return parts.join(' ');
|
|
97
97
|
}
|
|
98
|
-
import {
|
|
99
|
-
|
|
98
|
+
import { createHash } from 'node:crypto';
|
|
99
|
+
import { hostname } from 'node:os';
|
|
100
|
+
/** ORB-2171 - the checkout's token, derived exactly like the CLI and the skill wrapper derive theirs; `ORBOTO_AGENT_SESSION` wins. */
|
|
101
|
+
export function checkoutInstanceToken(env = process.env, host = hostname(), cwd = process.cwd()) {
|
|
102
|
+
const explicit = (env.ORBOTO_AGENT_SESSION ?? '').trim();
|
|
103
|
+
return explicit || `agent-${createHash('sha256').update(`${host}:${cwd}`).digest('hex').slice(0, 12)}`;
|
|
104
|
+
}
|
|
105
|
+
const MCP_PROCESS_INSTANCE = checkoutInstanceToken();
|
|
100
106
|
/** Precedence: explicit caller-supplied token > per-connection MCP session id
|
|
101
107
|
* (distinct per client even on a shared HTTP server) > per-process id (stdio). */
|
|
102
108
|
export function mcpInstanceToken(explicit, extra) {
|
|
@@ -163,3 +163,11 @@ describe('resolveByName', () => {
|
|
|
163
163
|
expect(ambiguous?.map((r) => r.id).sort()).toEqual(['m1', 'm3']);
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
|
+
describe('ORB-2171 - the stdio proxy shares the checkout token with the CLI and the skill wrapper', () => {
|
|
167
|
+
it('ORBOTO_AGENT_SESSION wins; otherwise agent-<sha256(hostname:cwd)[:12]>', async () => {
|
|
168
|
+
const { checkoutInstanceToken } = await import('./shared.js');
|
|
169
|
+
expect(checkoutInstanceToken({ ORBOTO_AGENT_SESSION: ' lane-7 ' }, 'host', '/repo')).toBe('lane-7');
|
|
170
|
+
expect(checkoutInstanceToken({}, 'build-host', '/srv/checkout')).toBe('agent-e0b18afc9cec');
|
|
171
|
+
expect(checkoutInstanceToken({}, 'build-host', '/srv/other')).not.toBe('agent-e0b18afc9cec');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -118,7 +118,7 @@ describe('orboto_work_next', () => {
|
|
|
118
118
|
expect(calls[0].method).toBe('POST');
|
|
119
119
|
expect(calls[0].url).toContain('/work-sessions/next');
|
|
120
120
|
expect(calls[0].body?.projectKey).toBe('ACME');
|
|
121
|
-
expect(String(calls[0].body?.agentSessionToken)).toMatch(/^
|
|
121
|
+
expect(String(calls[0].body?.agentSessionToken)).toMatch(/^agent-/);
|
|
122
122
|
const text = res.content[0].text;
|
|
123
123
|
expect(text).toContain('Reserved ACME-42');
|
|
124
124
|
expect(text).toContain('ws1');
|
|
@@ -89,7 +89,7 @@ describe('orboto_work_start', () => {
|
|
|
89
89
|
expect(post.method).toBe('POST');
|
|
90
90
|
expect(post.url).toContain('/work-sessions/start');
|
|
91
91
|
expect(post.body?.ticketId).toBe('t1');
|
|
92
|
-
expect(String(post.body?.agentSessionToken)).toMatch(/^
|
|
92
|
+
expect(String(post.body?.agentSessionToken)).toMatch(/^agent-/);
|
|
93
93
|
const text = res.content[0].text;
|
|
94
94
|
expect(text).toContain('Started a implementation work session on ACME-42');
|
|
95
95
|
expect(text).toContain('ws1');
|
|
@@ -58,7 +58,7 @@ describe('orboto_work_session_start', () => {
|
|
|
58
58
|
expect(post.method).toBe('POST');
|
|
59
59
|
expect(post.url).toContain('/work-sessions');
|
|
60
60
|
expect(post.body?.ticketId).toBe('t1');
|
|
61
|
-
expect(String(post.body?.agentSessionToken)).toMatch(/^
|
|
61
|
+
expect(String(post.body?.agentSessionToken)).toMatch(/^agent-/);
|
|
62
62
|
const text = res.content[0].text;
|
|
63
63
|
expect(text).toContain('Started a implementation work session on ACME-42');
|
|
64
64
|
expect(text).toContain('ws1');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orboto/mcp",
|
|
3
|
-
"version": "0.200.
|
|
3
|
+
"version": "0.200.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",
|