symposium-mcp 0.3.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/mcp.js +396 -149
  2. package/package.json +1 -1
package/dist/mcp.js CHANGED
@@ -3231,8 +3231,8 @@ var require_utils = __commonJS({
3231
3231
  }
3232
3232
  return ind;
3233
3233
  }
3234
- function removeDotSegments(path4) {
3235
- let input2 = path4;
3234
+ function removeDotSegments(path5) {
3235
+ let input2 = path5;
3236
3236
  const output2 = [];
3237
3237
  let nextSlash = -1;
3238
3238
  let len = 0;
@@ -3324,9 +3324,9 @@ var require_utils = __commonJS({
3324
3324
  const hex3 = input2.slice(i + 1, i + 3);
3325
3325
  if (isHexPair(hex3)) {
3326
3326
  const normalizedHex = hex3.toUpperCase();
3327
- const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3328
- if (decodeUnreserved && isUnreserved(decoded)) {
3329
- output2 += decoded;
3327
+ const decoded2 = String.fromCharCode(parseInt(normalizedHex, 16));
3328
+ if (decodeUnreserved && isUnreserved(decoded2)) {
3329
+ output2 += decoded2;
3330
3330
  } else {
3331
3331
  output2 += "%" + normalizedHex;
3332
3332
  }
@@ -3345,9 +3345,9 @@ var require_utils = __commonJS({
3345
3345
  const hex3 = input2.slice(i + 1, i + 3);
3346
3346
  if (isHexPair(hex3)) {
3347
3347
  const normalizedHex = hex3.toUpperCase();
3348
- const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3349
- if (decoded !== "." && isUnreserved(decoded)) {
3350
- output2 += decoded;
3348
+ const decoded2 = String.fromCharCode(parseInt(normalizedHex, 16));
3349
+ if (decoded2 !== "." && isUnreserved(decoded2)) {
3350
+ output2 += decoded2;
3351
3351
  } else {
3352
3352
  output2 += "%" + normalizedHex;
3353
3353
  }
@@ -3484,8 +3484,8 @@ var require_schemes = __commonJS({
3484
3484
  wsComponent.secure = void 0;
3485
3485
  }
3486
3486
  if (wsComponent.resourceName) {
3487
- const [path4, query] = wsComponent.resourceName.split("?");
3488
- wsComponent.path = path4 && path4 !== "/" ? path4 : void 0;
3487
+ const [path5, query] = wsComponent.resourceName.split("?");
3488
+ wsComponent.path = path5 && path5 !== "/" ? path5 : void 0;
3489
3489
  wsComponent.query = query;
3490
3490
  wsComponent.resourceName = void 0;
3491
3491
  }
@@ -6269,7 +6269,7 @@ var require_discriminator = __commonJS({
6269
6269
  error: error62,
6270
6270
  code(cxt) {
6271
6271
  const { gen, data, schema, parentSchema, it } = cxt;
6272
- const { oneOf } = parentSchema;
6272
+ const { oneOf: oneOf2 } = parentSchema;
6273
6273
  if (!it.opts.discriminator) {
6274
6274
  throw new Error("discriminator: requires discriminator option");
6275
6275
  }
@@ -6278,7 +6278,7 @@ var require_discriminator = __commonJS({
6278
6278
  throw new Error("discriminator: requires propertyName");
6279
6279
  if (schema.mapping)
6280
6280
  throw new Error("discriminator: mapping is not supported");
6281
- if (!oneOf)
6281
+ if (!oneOf2)
6282
6282
  throw new Error("discriminator: requires oneOf keyword");
6283
6283
  const valid = gen.let("valid", false);
6284
6284
  const tag = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`);
@@ -6306,8 +6306,8 @@ var require_discriminator = __commonJS({
6306
6306
  const oneOfMapping = {};
6307
6307
  const topRequired = hasRequired(parentSchema);
6308
6308
  let tagRequired = true;
6309
- for (let i = 0; i < oneOf.length; i++) {
6310
- let sch = oneOf[i];
6309
+ for (let i = 0; i < oneOf2.length; i++) {
6310
+ let sch = oneOf2[i];
6311
6311
  if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) {
6312
6312
  const ref = sch.$ref;
6313
6313
  sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, ref);
@@ -7274,8 +7274,8 @@ function getErrorMap() {
7274
7274
 
7275
7275
  // ../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/parseUtil.js
7276
7276
  var makeIssue = (params) => {
7277
- const { data, path: path4, errorMaps, issueData } = params;
7278
- const fullPath = [...path4, ...issueData.path || []];
7277
+ const { data, path: path5, errorMaps, issueData } = params;
7278
+ const fullPath = [...path5, ...issueData.path || []];
7279
7279
  const fullIssue = {
7280
7280
  ...issueData,
7281
7281
  path: fullPath
@@ -7390,11 +7390,11 @@ var errorUtil;
7390
7390
 
7391
7391
  // ../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/types.js
7392
7392
  var ParseInputLazyPath = class {
7393
- constructor(parent, value, path4, key) {
7393
+ constructor(parent, value, path5, key) {
7394
7394
  this._cachedPath = [];
7395
7395
  this.parent = parent;
7396
7396
  this.data = value;
7397
- this._path = path4;
7397
+ this._path = path5;
7398
7398
  this._key = key;
7399
7399
  }
7400
7400
  get path() {
@@ -7790,14 +7790,14 @@ function isValidJWT(jwt2, alg) {
7790
7790
  if (!header)
7791
7791
  return false;
7792
7792
  const base643 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "=");
7793
- const decoded = JSON.parse(atob(base643));
7794
- if (typeof decoded !== "object" || decoded === null)
7793
+ const decoded2 = JSON.parse(atob(base643));
7794
+ if (typeof decoded2 !== "object" || decoded2 === null)
7795
7795
  return false;
7796
- if ("typ" in decoded && decoded?.typ !== "JWT")
7796
+ if ("typ" in decoded2 && decoded2?.typ !== "JWT")
7797
7797
  return false;
7798
- if (!decoded.alg)
7798
+ if (!decoded2.alg)
7799
7799
  return false;
7800
- if (alg && decoded.alg !== alg)
7800
+ if (alg && decoded2.alg !== alg)
7801
7801
  return false;
7802
7802
  return true;
7803
7803
  } catch {
@@ -11349,10 +11349,10 @@ function mergeDefs(...defs) {
11349
11349
  function cloneDef(schema) {
11350
11350
  return mergeDefs(schema._zod.def);
11351
11351
  }
11352
- function getElementAtPath(obj, path4) {
11353
- if (!path4)
11352
+ function getElementAtPath(obj, path5) {
11353
+ if (!path5)
11354
11354
  return obj;
11355
- return path4.reduce((acc, key) => acc?.[key], obj);
11355
+ return path5.reduce((acc, key) => acc?.[key], obj);
11356
11356
  }
11357
11357
  function promiseAllObject(promisesObj) {
11358
11358
  const keys = Object.keys(promisesObj);
@@ -11692,11 +11692,11 @@ function explicitlyAborted(x, startIndex = 0) {
11692
11692
  }
11693
11693
  return false;
11694
11694
  }
11695
- function prefixIssues(path4, issues) {
11695
+ function prefixIssues(path5, issues) {
11696
11696
  return issues.map((iss) => {
11697
11697
  var _a3;
11698
11698
  (_a3 = iss).path ?? (_a3.path = []);
11699
- iss.path.unshift(path4);
11699
+ iss.path.unshift(path5);
11700
11700
  return iss;
11701
11701
  });
11702
11702
  }
@@ -12158,16 +12158,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
12158
12158
  }
12159
12159
  function formatError(error62, mapper = (issue2) => issue2.message) {
12160
12160
  const fieldErrors = { _errors: [] };
12161
- const processError = (error63, path4 = []) => {
12161
+ const processError = (error63, path5 = []) => {
12162
12162
  for (const issue2 of error63.issues) {
12163
12163
  if (issue2.code === "invalid_union" && issue2.errors.length) {
12164
- issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
12164
+ issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
12165
12165
  } else if (issue2.code === "invalid_key") {
12166
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
12166
+ processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
12167
12167
  } else if (issue2.code === "invalid_element") {
12168
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
12168
+ processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
12169
12169
  } else {
12170
- const fullpath = [...path4, ...issue2.path];
12170
+ const fullpath = [...path5, ...issue2.path];
12171
12171
  if (fullpath.length === 0) {
12172
12172
  fieldErrors._errors.push(mapper(issue2));
12173
12173
  } else {
@@ -12206,17 +12206,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
12206
12206
  }
12207
12207
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
12208
12208
  const result = { errors: [] };
12209
- const processError = (error63, path4 = []) => {
12209
+ const processError = (error63, path5 = []) => {
12210
12210
  var _a3;
12211
12211
  for (const issue2 of error63.issues) {
12212
12212
  if (issue2.code === "invalid_union" && issue2.errors.length) {
12213
- issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
12213
+ issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
12214
12214
  } else if (issue2.code === "invalid_key") {
12215
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
12215
+ processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
12216
12216
  } else if (issue2.code === "invalid_element") {
12217
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
12217
+ processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
12218
12218
  } else {
12219
- const fullpath = [...path4, ...issue2.path];
12219
+ const fullpath = [...path5, ...issue2.path];
12220
12220
  if (fullpath.length === 0) {
12221
12221
  result.errors.push(mapper(issue2));
12222
12222
  continue;
@@ -12255,8 +12255,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
12255
12255
  }
12256
12256
  function toDotPath(_path) {
12257
12257
  const segs = [];
12258
- const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12259
- for (const seg of path4) {
12258
+ const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12259
+ for (const seg of path5) {
12260
12260
  if (typeof seg === "number")
12261
12261
  segs.push(`[${seg}]`);
12262
12262
  else if (typeof seg === "symbol")
@@ -27807,11 +27807,11 @@ function normalizeObjectSchema(schema) {
27807
27807
  }
27808
27808
  return void 0;
27809
27809
  }
27810
- function getDotPath(path4) {
27811
- if (path4.length === 0) {
27810
+ function getDotPath(path5) {
27811
+ if (path5.length === 0) {
27812
27812
  return "object root";
27813
27813
  }
27814
- return path4.reduce((acc, seg, index) => {
27814
+ return path5.reduce((acc, seg, index) => {
27815
27815
  if (index === 0) {
27816
27816
  return String(seg);
27817
27817
  }
@@ -30038,13 +30038,13 @@ function resolveRef(ref, ctx) {
30038
30038
  if (!ref.startsWith("#")) {
30039
30039
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
30040
30040
  }
30041
- const path4 = ref.slice(1).split("/").filter(Boolean);
30042
- if (path4.length === 0) {
30041
+ const path5 = ref.slice(1).split("/").filter(Boolean);
30042
+ if (path5.length === 0) {
30043
30043
  return ctx.rootSchema;
30044
30044
  }
30045
30045
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
30046
- if (path4[0] === defsKey) {
30047
- const key = path4[1] === void 0 ? void 0 : decodeJSONPointerSegment(path4[1]);
30046
+ if (path5[0] === defsKey) {
30047
+ const key = path5[1] === void 0 ? void 0 : decodeJSONPointerSegment(path5[1]);
30048
30048
  if (!key || !ctx.defs[key]) {
30049
30049
  throw new Error(`Reference not found: ${ref}`);
30050
30050
  }
@@ -36301,6 +36301,7 @@ var StdioServerTransport = class {
36301
36301
 
36302
36302
  // ../lib/meeting/protocol.ts
36303
36303
  var MAX_DOC_PACKET_BYTES = 12 * 1024;
36304
+ var MAIN_DOC_ID = "main";
36304
36305
  var DOC_EDIT_OPS = ["append", "replace_section", "replace_all"];
36305
36306
  var MAX_WAIT_SECONDS = 50;
36306
36307
  var MAX_SURFACE_HTML_BYTES = 512 * 1024;
@@ -36315,6 +36316,15 @@ function agentIdOfKey(key) {
36315
36316
  }
36316
36317
  var PROPOSAL_PROGRESS = ["working", "done", "failed"];
36317
36318
  var REQUEST_STATUSES = ["acknowledged", "working", "done", "failed"];
36319
+ var WORK_KINDS = [
36320
+ "thinking",
36321
+ "searching",
36322
+ "writing",
36323
+ "drawing",
36324
+ "charting",
36325
+ "building"
36326
+ ];
36327
+ var AGENT_WORK_TTL_MS = 3 * 6e4;
36318
36328
  var PRESENCE_IDLE_AFTER_MS = 5 * 6e4;
36319
36329
  var SPACE_EVENT_TYPES = [
36320
36330
  "speech",
@@ -36324,6 +36334,7 @@ var SPACE_EVENT_TYPES = [
36324
36334
  "proposal",
36325
36335
  "proposal_decided",
36326
36336
  "doc_changed",
36337
+ "doc_removed",
36327
36338
  "ui_event",
36328
36339
  "state_changed",
36329
36340
  "surface_changed",
@@ -36675,8 +36686,8 @@ var SpaceClient = class {
36675
36686
  this.api = api.replace(/\/$/, "");
36676
36687
  this.base = `${this.api}/spaces/${encodeURIComponent(space2)}`;
36677
36688
  }
36678
- async request(method, path4, body, timeoutMs = 3e4) {
36679
- const resp = await fetch(this.base + path4, {
36689
+ async request(method, path5, body, timeoutMs = 3e4) {
36690
+ const resp = await fetch(this.base + path5, {
36680
36691
  method,
36681
36692
  headers: {
36682
36693
  Authorization: `Bearer ${this.key}`,
@@ -36693,7 +36704,7 @@ var SpaceClient = class {
36693
36704
  if (!resp.ok) {
36694
36705
  const detail = await resp.json().catch(() => null);
36695
36706
  throw new Error(
36696
- `${method} ${path4} failed (${resp.status}): ${detail?.error ?? resp.statusText}`
36707
+ `${method} ${path5} failed (${resp.status}): ${detail?.error ?? resp.statusText}`
36697
36708
  );
36698
36709
  }
36699
36710
  return resp.status === 204 ? void 0 : await resp.json();
@@ -36742,18 +36753,41 @@ var SpaceClient = class {
36742
36753
  postMessage(text2) {
36743
36754
  return this.request("POST", "/timeline", { text: text2 });
36744
36755
  }
36745
- // --- shared document -------------------------------------------------------------
36746
- /** The space's shared document, as Markdown. */
36747
- async readDocument() {
36748
- return (await this.request("GET", "/doc")).markdown;
36756
+ // --- shared documents ------------------------------------------------------------
36757
+ /** The space's shared documents, the one it started with (`main`) first. */
36758
+ async listDocuments() {
36759
+ return (await this.request("GET", "/docs")).docs;
36760
+ }
36761
+ /** Add a document to the space, optionally with its first text in Markdown. */
36762
+ createDocument(input2) {
36763
+ return this.request("POST", "/docs", input2);
36749
36764
  }
36750
36765
  /**
36751
- * Edit the shared document. People may be typing in it at the same time, so prefer
36766
+ * One of the shared documents, as Markdown (`main` unless you name another). People see
36767
+ * this agent on the document's tab for a little while afterwards.
36768
+ */
36769
+ async readDocument(id = MAIN_DOC_ID) {
36770
+ return (await this.request("GET", `/docs/${encodeURIComponent(id)}`)).markdown;
36771
+ }
36772
+ /**
36773
+ * Edit a shared document. People may be typing in it at the same time, so prefer
36752
36774
  * `append` or `replace_section` (which only touch part of it) to `replace_all`.
36753
- * Returns the document after the edit, as Markdown.
36775
+ * What the edit adds or changes is shown to everyone as written by this agent, until a
36776
+ * person edits it. Returns the document after the edit, as Markdown.
36754
36777
  */
36755
- async editDocument(edit) {
36756
- return (await this.request("POST", "/doc/edits", edit)).markdown;
36778
+ async editDocument(edit, id = MAIN_DOC_ID) {
36779
+ return (await this.request(
36780
+ "POST",
36781
+ `/docs/${encodeURIComponent(id)}/edits`,
36782
+ edit
36783
+ )).markdown;
36784
+ }
36785
+ renameDocument(id, title) {
36786
+ return this.request("PATCH", `/docs/${encodeURIComponent(id)}`, { title });
36787
+ }
36788
+ /** Remove a document this agent made. The first document (`main`) cannot be removed. */
36789
+ removeDocument(id) {
36790
+ return this.request("DELETE", `/docs/${encodeURIComponent(id)}`);
36757
36791
  }
36758
36792
  // --- proposals -------------------------------------------------------------------
36759
36793
  /**
@@ -36770,14 +36804,16 @@ var SpaceClient = class {
36770
36804
  }
36771
36805
  // --- attention -------------------------------------------------------------------
36772
36806
  /**
36773
- * Bring everyone's stage to one tab: a surface (yours or anyone's), the document or
36807
+ * Bring everyone's stage to one tab: a surface (yours or anyone's), a document or
36774
36808
  * Agents. People are switched to it, with a note saying you did it and a way to go
36775
36809
  * back. Use it when you have put up something everyone should look at now. The
36776
36810
  * conversation is a column beside the stage, not a tab: that target opens it for
36777
- * anyone who has closed it.
36811
+ * anyone who has closed it. `size` asks for it large: `expanded` gives the stage the
36812
+ * screen, `alone` opens a surface on its own instead of as a tile among the others.
36813
+ * Nobody's stage is made smaller.
36778
36814
  */
36779
- bringEveryoneTo(target) {
36780
- return this.request("POST", "/focus", { target });
36815
+ bringEveryoneTo(target, size) {
36816
+ return this.request("POST", "/focus", { target, size });
36781
36817
  }
36782
36818
  // --- requests --------------------------------------------------------------------
36783
36819
  /** This agent's id, as it appears in `request` events' `to` list. */
@@ -36788,10 +36824,23 @@ var SpaceClient = class {
36788
36824
  * Tell everyone in the space how you are getting on with a request addressed to you
36789
36825
  * (a `request` event; its `seq` is the request id). People see this next to the
36790
36826
  * request: `acknowledged` as soon as you have seen it, `working` (with a short note
36791
- * on what you are doing) while you act, then `done` or `failed`.
36827
+ * on what you are doing) while you act, then `done` or `failed`. `surfaceId` says
36828
+ * which surface the work is on: people see this agent on that tab meanwhile. `doing`
36829
+ * says what kind of work it is, which the agent's figure shows. `doc` says which shared
36830
+ * document the work is in, and under which heading: people see this agent on that
36831
+ * document's tab, and beside that section in the text.
36792
36832
  */
36793
- updateRequest(id, status, note) {
36794
- return this.request("PATCH", `/requests/${id}`, { status, note });
36833
+ updateRequest(id, status, note, surfaceId, doing, doc) {
36834
+ return this.request("PATCH", `/requests/${id}`, { status, note, surfaceId, doing, ...doc });
36835
+ }
36836
+ /**
36837
+ * Show the space what this agent is working on when it is not an answer to a request.
36838
+ * Its figure plays the matching animation for everyone, on the surface's or the
36839
+ * document's tab if one is named. Say it again to renew or change it (it lapses after a few minutes), and
36840
+ * pass `done` when finished. `key` tells several pieces of work apart.
36841
+ */
36842
+ setWork(work) {
36843
+ return this.request("PUT", "/work", work);
36795
36844
  }
36796
36845
  // --- events ----------------------------------------------------------------------
36797
36846
  /** How far through the event log this client has read: save it to carry on later. */
@@ -36893,22 +36942,71 @@ function agentName(client2, configured = process.env.SYMPOSIUM_AGENT_NAME) {
36893
36942
 
36894
36943
  // src/setup.ts
36895
36944
  import { spawn, spawnSync } from "node:child_process";
36896
- import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
36945
+ import { copyFileSync, existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
36897
36946
  import { homedir as homedir2, hostname as hostname3 } from "node:os";
36898
- import path2 from "node:path";
36947
+ import path3 from "node:path";
36899
36948
  import { createInterface } from "node:readline/promises";
36900
36949
  import { fileURLToPath } from "node:url";
36901
36950
 
36951
+ // src/command.ts
36952
+ import { existsSync } from "node:fs";
36953
+ import path from "node:path";
36954
+ function whereIs(command, env = process.env, exists = existsSync) {
36955
+ const extensions = (env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
36956
+ for (const dir of (env.PATH ?? env.Path ?? "").split(";")) {
36957
+ if (!dir) continue;
36958
+ for (const extension of extensions) {
36959
+ const file3 = path.win32.join(dir, command + extension.toLowerCase());
36960
+ if (exists(file3)) return file3;
36961
+ }
36962
+ }
36963
+ return void 0;
36964
+ }
36965
+ var quoted = (arg) => /^[\w\-./:\\=@]+$/.test(arg) ? arg : `"${arg.replace(/["%]/g, "")}"`;
36966
+ function starting(command, args, platform = process.platform, find = whereIs) {
36967
+ if (platform !== "win32") return { file: command, args, shell: false };
36968
+ const file3 = find(command);
36969
+ if (!file3) return void 0;
36970
+ if (!/\.(cmd|bat)$/i.test(file3)) return { file: file3, args, shell: false };
36971
+ return { file: [file3, ...args].map(quoted).join(" "), args: [], shell: true };
36972
+ }
36973
+ function relaxed(text2) {
36974
+ let out = "";
36975
+ for (let i = 0; i < text2.length; i++) {
36976
+ const c = text2[i];
36977
+ if (c === '"') {
36978
+ const start2 = i;
36979
+ for (i++; i < text2.length && text2[i] !== '"'; i++) if (text2[i] === "\\") i++;
36980
+ out += text2.slice(start2, i + 1);
36981
+ } else if (c === "/" && text2[i + 1] === "/") {
36982
+ while (i < text2.length && text2[i] !== "\n") i++;
36983
+ out += "\n";
36984
+ } else if (c === "/" && text2[i + 1] === "*") {
36985
+ const end = text2.indexOf("*/", i + 2);
36986
+ i = end < 0 ? text2.length : end + 1;
36987
+ } else if (c === "}" || c === "]") {
36988
+ out = out.replace(/,\s*$/, "") + c;
36989
+ } else {
36990
+ out += c;
36991
+ }
36992
+ }
36993
+ return out;
36994
+ }
36995
+ function decoded(bytes) {
36996
+ if (bytes[0] === 255 && bytes[1] === 254) return bytes.subarray(2).toString("utf16le");
36997
+ return bytes.toString("utf8").replace(/^/, "");
36998
+ }
36999
+
36902
37000
  // src/remembered.ts
36903
37001
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
36904
37002
  import { homedir } from "node:os";
36905
- import path from "node:path";
37003
+ import path2 from "node:path";
36906
37004
  var FIRST_AGENT = "Claude";
36907
37005
  function stateFile(agent2, home = homedir(), override = process.env.SYMPOSIUM_STATE_FILE) {
36908
37006
  if (override) return override;
36909
37007
  const slug = agent2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
36910
37008
  const name = slug === FIRST_AGENT.toLowerCase() ? "mcp.json" : `mcp.${slug || "agent"}.json`;
36911
- return path.join(home, ".symposium", name);
37009
+ return path2.join(home, ".symposium", name);
36912
37010
  }
36913
37011
  var agent = FIRST_AGENT;
36914
37012
  function rememberAs(name) {
@@ -36926,7 +37024,7 @@ function load() {
36926
37024
  function save(next) {
36927
37025
  try {
36928
37026
  const target = file2();
36929
- mkdirSync(path.dirname(target), { recursive: true, mode: 448 });
37027
+ mkdirSync(path2.dirname(target), { recursive: true, mode: 448 });
36930
37028
  const temp = `${target}.${process.pid}.tmp`;
36931
37029
  writeFileSync(temp, JSON.stringify(next), { mode: 384 });
36932
37030
  renameSync(temp, target);
@@ -36969,12 +37067,19 @@ function rememberAccount(account2) {
36969
37067
  var SERVER = "symposium";
36970
37068
  var realHost = {
36971
37069
  run(command, args) {
36972
- const done = spawnSync(command, args, {
36973
- stdio: "ignore",
36974
- shell: process.platform === "win32",
37070
+ const how = starting(command, args);
37071
+ if (!how) return void 0;
37072
+ const done = spawnSync(how.file, how.args, {
37073
+ stdio: ["ignore", "ignore", "pipe"],
37074
+ encoding: "utf8",
37075
+ shell: how.shell,
36975
37076
  timeout: 6e4
36976
37077
  });
36977
- return done.error ? void 0 : done.status ?? 1;
37078
+ if (done.error) {
37079
+ const missing = done.error.code === "ENOENT";
37080
+ return missing ? void 0 : { status: 1, said: done.error.message };
37081
+ }
37082
+ return { status: done.status ?? 1, said: lastLine(done.stderr ?? "") };
36978
37083
  },
36979
37084
  redeem: redeemConnectFor,
36980
37085
  join: (account2, space2) => new AccountClient(account2.api, account2.token).join(space2),
@@ -36996,13 +37101,12 @@ var realHost = {
36996
37101
  },
36997
37102
  open(command, args) {
36998
37103
  return new Promise((resolve) => {
37104
+ const how = starting(command, args);
37105
+ if (!how) return resolve();
36999
37106
  const ignore = () => {
37000
37107
  };
37001
37108
  process.on("SIGINT", ignore);
37002
- const child = spawn(command, args, {
37003
- stdio: "inherit",
37004
- shell: process.platform === "win32"
37005
- });
37109
+ const child = spawn(how.file, how.args, { stdio: "inherit", shell: how.shell });
37006
37110
  const done = () => {
37007
37111
  process.off("SIGINT", ignore);
37008
37112
  resolve();
@@ -37012,55 +37116,78 @@ var realHost = {
37012
37116
  });
37013
37117
  }
37014
37118
  };
37015
- function serverCommand(self = fileURLToPath(import.meta.url)) {
37016
- return self.endsWith(".ts") ? ["npx", "-y", "tsx", path2.join(path2.dirname(self), "mcp.ts")] : ["npx", "-y", "symposium-mcp"];
37119
+ function lastLine(output2) {
37120
+ const lines = output2.replace(/\x1b\[[0-9;]*m/g, "").split("\n").map((line) => line.trim()).filter(Boolean);
37121
+ return (lines.at(-1) ?? "").slice(0, 200);
37122
+ }
37123
+ function serverCommand(self = fileURLToPath(import.meta.url), platform = process.platform) {
37124
+ const npx = self.endsWith(".ts") ? ["npx", "-y", "tsx", path3.join(path3.dirname(self), "mcp.ts")] : ["npx", "-y", "symposium-mcp"];
37125
+ return platform === "win32" ? ["cmd", "/c", ...npx] : npx;
37017
37126
  }
37018
37127
  var withCommand = (label, agent2, bin, add) => ({
37019
37128
  label,
37020
37129
  agent: agent2,
37021
- found: (host) => host.run(bin, ["--version"]) !== void 0,
37130
+ // Installed and working: a command that cannot say its version cannot be set up either.
37131
+ found: (host) => host.run(bin, ["--version"])?.status === 0,
37022
37132
  register(host, command) {
37023
- if (host.run(bin, ["mcp", "get", SERVER]) === 0) return "present";
37024
- if (host.run(bin, add(command)) !== 0) throw new Error(`\`${bin} mcp add\` failed`);
37025
- return "added";
37133
+ if (host.run(bin, ["mcp", "get", SERVER])?.status === 0) return { tools: "present" };
37134
+ const added = host.run(bin, add(command));
37135
+ if (added?.status !== 0) {
37136
+ throw new Error(`\`${bin} mcp add\` failed${added?.said ? ` (${added.said})` : ""}`);
37137
+ }
37138
+ return { tools: "added" };
37026
37139
  },
37027
37140
  start: (prompt) => [bin, prompt]
37028
37141
  });
37029
- var withConfigFile = (label, agent2, dir, file3) => ({
37030
- label,
37031
- agent: agent2,
37032
- found: () => existsSync(path2.join(homedir2(), ...dir)),
37033
- register(_host, command) {
37034
- const target = path2.join(homedir2(), ...dir, file3);
37035
- let settings = {};
37036
- if (existsSync(target)) {
37037
- let parsed;
37038
- try {
37039
- parsed = JSON.parse(readFileSync2(target, "utf8"));
37040
- } catch {
37041
- parsed = null;
37042
- }
37043
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
37044
- throw new Error(`${target} is not plain JSON; add the server to it by hand`);
37045
- }
37046
- settings = parsed;
37047
- }
37048
- const servers = settings.mcpServers;
37049
- const existing = typeof servers === "object" && servers !== null && !Array.isArray(servers) ? servers : {};
37050
- if (SERVER in existing) return "present";
37051
- settings.mcpServers = {
37052
- ...existing,
37053
- [SERVER]: {
37054
- command: command[0],
37055
- args: command.slice(1),
37056
- env: { SYMPOSIUM_AGENT_NAME: agent2 }
37142
+ var withConfigFile = (label, agent2, dir, file3) => {
37143
+ const entry = (command) => ({
37144
+ command: command[0],
37145
+ args: command.slice(1),
37146
+ env: { SYMPOSIUM_AGENT_NAME: agent2 }
37147
+ });
37148
+ const target = () => path3.join(homedir2(), ...dir, file3);
37149
+ return {
37150
+ label,
37151
+ agent: agent2,
37152
+ found: () => existsSync2(path3.join(homedir2(), ...dir)),
37153
+ register(_host, command) {
37154
+ let settings = {};
37155
+ let loose = false;
37156
+ const text2 = existsSync2(target()) ? decoded(readFileSync2(target())) : "";
37157
+ if (text2.trim()) {
37158
+ let parsed;
37159
+ try {
37160
+ parsed = JSON.parse(text2);
37161
+ } catch {
37162
+ loose = true;
37163
+ try {
37164
+ parsed = JSON.parse(relaxed(text2));
37165
+ } catch {
37166
+ parsed = null;
37167
+ }
37168
+ }
37169
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
37170
+ throw new Error(`${target()} could not be read as JSON`);
37171
+ }
37172
+ settings = parsed;
37057
37173
  }
37058
- };
37059
- writeFileSync2(target, `${JSON.stringify(settings, null, 2)}
37174
+ const servers = settings.mcpServers;
37175
+ const existing = typeof servers === "object" && servers !== null && !Array.isArray(servers) ? servers : {};
37176
+ if (SERVER in existing) return { tools: "present" };
37177
+ const kept = `${target()}.before-symposium`;
37178
+ if (loose) copyFileSync(target(), kept);
37179
+ settings.mcpServers = { ...existing, [SERVER]: entry(command) };
37180
+ writeFileSync2(target(), `${JSON.stringify(settings, null, 2)}
37060
37181
  `);
37061
- return "added";
37062
- }
37063
- });
37182
+ return {
37183
+ tools: "added",
37184
+ note: loose ? `the file's comments are kept in ${path3.basename(kept)}` : void 0
37185
+ };
37186
+ },
37187
+ byHand: (command) => `${label}: add this inside "mcpServers" in ${target()}
37188
+ "${SERVER}": ${JSON.stringify(entry(command))}`
37189
+ };
37190
+ };
37064
37191
  var TARGETS = [
37065
37192
  withCommand("Claude Code", "Claude", "claude", (command) => [
37066
37193
  "mcp",
@@ -37096,7 +37223,7 @@ async function setup(argv, host = realHost) {
37096
37223
  }
37097
37224
  const rows = found.map((target) => {
37098
37225
  try {
37099
- return { target, tools: target.register(host, command), note: "" };
37226
+ return { target, note: "", ...target.register(host, command) };
37100
37227
  } catch (e) {
37101
37228
  return { target, tools: "failed", note: reason(e) };
37102
37229
  }
@@ -37150,30 +37277,49 @@ async function setup(argv, host = realHost) {
37150
37277
  for (const row of rows) {
37151
37278
  const tools = row.tools === "added" ? "tools added" : row.tools === "present" ? "tools already there" : `could not add the tools: ${row.note}`;
37152
37279
  const link = row.tools === "failed" ? void 0 : connection.get(row.target.agent);
37153
- host.say(` ${row.target.label.padEnd(width)} ${[tools, link].filter(Boolean).join(", ")}`);
37280
+ const note = row.tools !== "failed" && row.note ? `(${row.note})` : void 0;
37281
+ host.say(
37282
+ ` ${row.target.label.padEnd(width)} ${[tools, link].filter(Boolean).join(", ")}${note ? ` ${note}` : ""}`
37283
+ );
37154
37284
  }
37155
37285
  host.say("");
37286
+ const byHand = rows.filter((row) => row.tools === "failed" && row.target.byHand);
37287
+ for (const row of byHand) host.say(row.target.byHand(command));
37288
+ if (byHand.length) host.say("");
37289
+ const working = rows.filter((row) => row.tools !== "failed");
37156
37290
  if (!code) {
37157
- host.say('To connect them: press "Connect your agent" in Symposium and run the line it shows.');
37291
+ if (working.length) {
37292
+ host.say(
37293
+ 'To connect them: press "Connect your agent" in Symposium and run the line it shows.'
37294
+ );
37295
+ }
37158
37296
  return failed ? 1 : 0;
37159
37297
  }
37160
- const ready = rows.filter(
37161
- (row) => row.tools !== "failed" && row.target.start && paired(row.target.agent)
37162
- );
37298
+ const connected = working.filter((row) => paired(row.target.agent));
37299
+ if (connected.length === 0) {
37300
+ host.say(
37301
+ 'No agent was connected. Once the above is sorted out, press "Connect your agent" in Symposium again and run the new line.'
37302
+ );
37303
+ return 1;
37304
+ }
37305
+ const ready = connected.filter((row) => row.target.start);
37163
37306
  const chosen = ready.length ? await choose(
37164
37307
  host,
37165
37308
  ready.map((row) => row.target)
37166
37309
  ) : void 0;
37167
37310
  if (!chosen) {
37168
- host.say('Start a new session in any of them and say "join my meeting".');
37311
+ host.say(
37312
+ `Start a new session in ${oneOf(connected.map((row) => row.target.label))} and say "join my meeting".`
37313
+ );
37169
37314
  return failed ? 1 : 0;
37170
37315
  }
37171
37316
  const [bin, ...args] = chosen.start(
37172
- code.space ? `Join my Symposium meeting in the space ${code.space}.` : "Join my Symposium meeting."
37317
+ code.space && /^[\w .-]+$/.test(code.space) ? `Join my Symposium meeting in the space ${code.space}.` : "Join my Symposium meeting."
37173
37318
  );
37174
37319
  await host.open(bin, args);
37175
37320
  return failed ? 1 : 0;
37176
37321
  }
37322
+ var oneOf = (names) => names.length < 2 ? names.join("") : `${names.slice(0, -1).join(", ")} or ${names.at(-1)}`;
37177
37323
  async function choose(host, targets) {
37178
37324
  const question = targets.length === 1 ? `Open ${targets[0].label} in the meeting now? [Y/n] ` : `Open one in the meeting now? ${targets.map((t, i) => `${i + 1}) ${t.label}`).join(" ")} [1, or n for none] `;
37179
37325
  const answer = (await host.ask(question))?.trim().toLowerCase();
@@ -37184,7 +37330,7 @@ async function choose(host, targets) {
37184
37330
 
37185
37331
  // src/wake.ts
37186
37332
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
37187
- import path3 from "node:path";
37333
+ import path4 from "node:path";
37188
37334
  import { pathToFileURL } from "node:url";
37189
37335
  function wakeEvents(events, agentId, now, maxAgeMs) {
37190
37336
  return events.filter(
@@ -37201,8 +37347,8 @@ var minutes = (name, fallback) => {
37201
37347
  };
37202
37348
  function positionFile(client2) {
37203
37349
  const safe = (s) => s.replace(/[^\w-]/g, "_");
37204
- return path3.join(
37205
- path3.dirname(stateFile("")),
37350
+ return path4.join(
37351
+ path4.dirname(stateFile("")),
37206
37352
  `wake-${safe(client2.space)}-${safe(client2.agentId)}`
37207
37353
  );
37208
37354
  }
@@ -37217,7 +37363,7 @@ function readPosition(file3) {
37217
37363
  function savePosition(file3, position) {
37218
37364
  if (position === void 0) return;
37219
37365
  try {
37220
- mkdirSync2(path3.dirname(file3), { recursive: true, mode: 448 });
37366
+ mkdirSync2(path4.dirname(file3), { recursive: true, mode: 448 });
37221
37367
  const temp = `${file3}.${process.pid}.tmp`;
37222
37368
  writeFileSync3(temp, String(position), { mode: 384 });
37223
37369
  renameSync2(temp, file3);
@@ -37290,7 +37436,7 @@ async function main() {
37290
37436
  console.error("Nothing arrived in time; start it again.");
37291
37437
  return 3;
37292
37438
  }
37293
- if (process.argv[1] && path3.basename(process.argv[1]).startsWith("wake.") && import.meta.url === pathToFileURL(process.argv[1]).href) {
37439
+ if (process.argv[1] && path4.basename(process.argv[1]).startsWith("wake.") && import.meta.url === pathToFileURL(process.argv[1]).href) {
37294
37440
  main().then(
37295
37441
  (code) => process.exit(code),
37296
37442
  (e) => {
@@ -37376,6 +37522,10 @@ exits when a request arrives: if you can run background commands, start it whene
37376
37522
  stop calling wait_for_events (before ending your turn, or before a long piece of other
37377
37523
  work), and start it again after handling what it printed.
37378
37524
 
37525
+ Everyone in the space sees you as a small figure that shows what you are doing. Keep it
37526
+ true: pass \`doing\` (and \`surfaceId\` once there is one) to update_request while you
37527
+ work on a request, and call working_on for anything else you do in the space.
37528
+
37379
37529
  When the user pastes a connect code \u2014 a line like "Connect to my Symposium: symc_\u2026" \u2014
37380
37530
  call connect with what they pasted, straight away. It links you to their account for
37381
37531
  good (and joins the space it was made in, if any).
@@ -37461,8 +37611,8 @@ async function enter(next, invite) {
37461
37611
  space: snap.id,
37462
37612
  ...snap.name ? { name: snap.name } : {},
37463
37613
  you: snap.agents.find((a) => a.id === target.agentId)?.name ?? target.agentId,
37464
- liveParticipants: snap.participants,
37465
- presence: snap.presence,
37614
+ liveParticipants: withoutPhotos(snap.participants),
37615
+ presence: withoutPhotos(snap.presence),
37466
37616
  surfaces: snap.surfaces.map((s) => ({ id: s.id, title: s.title })),
37467
37617
  ...unanswered.length ? {
37468
37618
  unansweredRequests: unanswered,
@@ -37475,6 +37625,7 @@ async function enter(next, invite) {
37475
37625
  remembered: remembered ? "Later sessions start in this space. No configuration change is needed." : "Could not save this space; later sessions will need to join again."
37476
37626
  };
37477
37627
  }
37628
+ var withoutPhotos = (people) => people.map((person) => ({ ...person, picture: void 0 }));
37478
37629
  var text = (value) => ({
37479
37630
  content: [
37480
37631
  {
@@ -37490,6 +37641,13 @@ var LINE_LABELS = {
37490
37641
  request: " (request to agents)",
37491
37642
  proposal: " (agent proposal)"
37492
37643
  };
37644
+ var DOING = 'The kind of work, which your figure in the room shows: "thinking" (working something out), "searching" (looking things up, reading), "writing" (text, the shared document), "drawing" (making an image or illustration), "charting" (charts, numbers, dashboards), "building" (putting a surface or other UI together).';
37645
+ var WORK_DOCUMENT = external_exports.string().optional().describe(
37646
+ `The shared document this work is in, if any: its id from list_documents ("main" for the first). You are shown on that document's tab while you work.`
37647
+ );
37648
+ var WORK_SECTION = external_exports.string().max(200).optional().describe(
37649
+ "With documentId: the text of the heading you are writing under. People see you beside that section in the document, which tells them not to type there while you work."
37650
+ );
37493
37651
  var fmtTime = (ts) => new Date(ts).toISOString().slice(0, 19).replace("T", " ");
37494
37652
  var server = new McpServer(
37495
37653
  { name: "symposium", version: "0.2.0" },
@@ -37654,14 +37812,15 @@ server.registerTool(
37654
37812
  const snap = await space().snapshot();
37655
37813
  return text({
37656
37814
  space: snap.id,
37657
- liveParticipants: snap.participants,
37658
- presence: snap.presence,
37815
+ liveParticipants: withoutPhotos(snap.participants),
37816
+ presence: withoutPhotos(snap.presence),
37659
37817
  surfaces: snap.surfaces.map((s) => ({
37660
37818
  id: s.id,
37661
37819
  title: s.title,
37662
37820
  by: s.agentName,
37663
37821
  state: s.state
37664
37822
  })),
37823
+ documents: (snap.docs ?? []).map((d) => ({ id: d.id, title: d.title })),
37665
37824
  you: space().agentId,
37666
37825
  agents: snap.agents.map((a) => ({ id: a.id, name: a.name, owner: a.owner })),
37667
37826
  latestSeq: snap.seq
@@ -37823,7 +37982,7 @@ server.registerTool(
37823
37982
  "- request: someone asked agents to do something. `to` lists the agent ids it is for and `seq` is its id; if yours is in `to`, call update_request right away.",
37824
37983
  '- proposal_decided: a person approved or dismissed a proposal. If `agentId` is yours and `decision` is "approved", go ahead and report with update_proposal.',
37825
37984
  "- proposal: another agent offered to do something.",
37826
- "- doc_changed: the shared document changed (at most every 30 seconds); call read_document to see it.",
37985
+ "- doc_changed: a shared document (`docId`, `title`) was edited (at most every 30 seconds per document), renamed or, with `created`, made; call read_document with that id to see it. doc_removed: one was removed.",
37827
37986
  "- speech, chat, message: what people said, what they typed, what other agents posted.",
37828
37987
  "- ui_event (meeting.emit from a surface), state_changed (surface state), surface_changed / surface_removed (another agent edited the stage).",
37829
37988
  'When you are connected to their account, it can instead return a `summons`: the user pressed "Bring my agent" in a space; join_space there.'
@@ -37888,38 +38047,73 @@ server.registerTool(
37888
38047
  server.registerTool(
37889
38048
  "bring_everyone_to",
37890
38049
  {
37891
- description: "Switch everyone's stage to one tab so they are all looking at the same thing: one surface (pass its id; it can be yours or another agent's) or the shared document. Each person sees a note saying you brought them there and can go back. With neither, it opens the conversation column for anyone who has closed it; the conversation is beside the stage, not a tab on it, so this is rarely needed. Use it sparingly: when you have just put up or updated something the whole room should look at now, or someone asked you to show something. Do not use it for every small update.",
38050
+ description: "Switch everyone's stage to one tab so they are all looking at the same thing: one surface (pass its id; it can be yours or another agent's) or one shared document. Each person sees a note saying you brought them there and can go back. With neither, it opens the conversation column for anyone who has closed it; the conversation is beside the stage, not a tab on it, so this is rarely needed. Use it sparingly: when you have just put up or updated something the whole room should look at now, or someone asked you to show something. Do not use it for every small update.",
37892
38051
  inputSchema: {
37893
38052
  surfaceId: external_exports.string().optional().describe("Surface to show; omit to open the conversation"),
37894
- document: external_exports.boolean().optional().describe("Set true to show the shared document instead")
38053
+ documentId: external_exports.string().optional().describe("A shared document to show instead: its id from list_documents"),
38054
+ document: external_exports.boolean().optional().describe('Set true to show the first shared document ("main") instead')
37895
38055
  }
37896
38056
  },
37897
- async ({ surfaceId, document }) => {
38057
+ async ({ surfaceId, documentId, document }) => {
37898
38058
  const result = await space().bringEveryoneTo(
37899
- document ? { kind: "document" } : surfaceId ? { kind: "surface", id: surfaceId } : { kind: "conversation" }
38059
+ documentId || document ? { kind: "document", id: documentId || MAIN_DOC_ID } : surfaceId ? { kind: "surface", id: surfaceId } : { kind: "conversation" }
37900
38060
  );
37901
38061
  return text({ showing: result.target });
37902
38062
  }
37903
38063
  );
38064
+ var DOCUMENT = external_exports.string().optional().describe(
38065
+ `Which document: its id from list_documents. Omit for the first one ("main"), the space's notes.`
38066
+ );
37904
38067
  server.registerTool(
37905
- "read_document",
38068
+ "list_documents",
37906
38069
  {
37907
- description: "Read the space's shared document as Markdown. Everyone in the space can edit it together, in a meeting or between meetings; it is where notes, agendas, decisions and drafts live. Read it before editing so you know its headings.",
38070
+ description: `List the shared documents in the space: each one's id and title, who made it, when it last changed, and which agent last wrote in it. Every space has the document "main" (its notes); people and agents add others beside it, each on its own tab of the stage.`,
37908
38071
  inputSchema: {}
37909
38072
  },
37910
- async () => text(await space().readDocument() || "(the document is empty)")
38073
+ async () => text(
38074
+ (await space().listDocuments()).map((d) => ({
38075
+ id: d.id,
38076
+ title: d.title,
38077
+ ...d.createdBy ? { madeBy: d.createdBy.name } : {},
38078
+ ...d.updatedAt ? { changed: fmtTime(d.updatedAt) } : {},
38079
+ ...d.agentEdit ? { lastAgentEdit: d.agentEdit.name } : {}
38080
+ }))
38081
+ )
38082
+ );
38083
+ server.registerTool(
38084
+ "create_document",
38085
+ {
38086
+ description: 'Add a shared document to the space, on a tab of its own, optionally with its first text in Markdown. Use it for a piece of writing that stands by itself (a draft, a brief, a write-up, minutes of one meeting) instead of putting it into the notes people are keeping in "main". Returns its id, which read_document, edit_document and bring_everyone_to take.',
38087
+ inputSchema: {
38088
+ title: external_exports.string().max(80).describe('Short tab title, e.g. "Launch brief"'),
38089
+ markdown: external_exports.string().optional().describe("Its first text")
38090
+ }
38091
+ },
38092
+ async ({ title, markdown }) => {
38093
+ const doc = await space().createDocument({ title, markdown });
38094
+ return text({ id: doc.id, title: doc.title });
38095
+ }
38096
+ );
38097
+ server.registerTool(
38098
+ "read_document",
38099
+ {
38100
+ description: "Read one of the space's shared documents as Markdown. Everyone in the space can edit them together, in a meeting or between meetings; they are where notes, agendas, decisions and drafts live. Read a document before editing it so you know its headings. People see you on the document's tab for a little while after you read it.",
38101
+ inputSchema: { document: DOCUMENT }
38102
+ },
38103
+ async ({ document }) => text(await space().readDocument(document) || "(the document is empty)")
37911
38104
  );
37912
38105
  server.registerTool(
37913
38106
  "edit_document",
37914
38107
  {
37915
- description: 'Edit the shared document, in Markdown (headings, lists, bold, italic, links, quotes, code blocks). People may be typing in it at the same moment, so make the narrowest edit that does the job: "append" adds to the end; "replace_section" replaces everything under one heading, keeping the heading, and creates the section at the end if the heading does not exist yet; "replace_all" rewrites the whole document and should be used only when asked to. Returns the document after your edit.',
38108
+ description: 'Edit a shared document, in Markdown (headings, lists, bold, italic, links, quotes, code blocks). People may be typing in it at the same moment, so make the narrowest edit that does the job: "append" adds to the end; "replace_section" replaces everything under one heading, keeping the heading, and creates the section at the end if the heading does not exist yet; "replace_all" rewrites the whole document and should be used only when asked to. Everyone sees what you added or changed marked as written by you until a person edits it; text you send back unchanged is not marked, so send the rest of a section back exactly as it was. Before you start composing anything that takes more than a moment, say where you are about to write with update_request (or working_on): doing "writing", `documentId` and, for replace_section, `section`. People then see you beside that section and know not to type in it. Returns the document after your edit.',
37916
38109
  inputSchema: {
38110
+ document: DOCUMENT,
37917
38111
  op: external_exports.enum(DOC_EDIT_OPS),
37918
38112
  heading: external_exports.string().optional().describe("For replace_section: the text of the heading whose section to replace"),
37919
38113
  markdown: external_exports.string().describe("The new content")
37920
38114
  }
37921
38115
  },
37922
- async ({ op, heading, markdown }) => {
38116
+ async ({ document, op, heading, markdown }) => {
37923
38117
  if (op === "replace_section" && !heading) {
37924
38118
  throw new Error(
37925
38119
  "replace_section needs `heading`: the text of the heading whose section to replace."
@@ -37927,11 +38121,23 @@ server.registerTool(
37927
38121
  }
37928
38122
  return text(
37929
38123
  await space().editDocument(
37930
- op === "replace_section" ? { op, heading, markdown } : { op, markdown }
38124
+ op === "replace_section" ? { op, heading, markdown } : { op, markdown },
38125
+ document
37931
38126
  )
37932
38127
  );
37933
38128
  }
37934
38129
  );
38130
+ server.registerTool(
38131
+ "remove_document",
38132
+ {
38133
+ description: 'Remove a shared document you made, for good. You cannot remove documents other people or agents made, nor the first one ("main").',
38134
+ inputSchema: { document: external_exports.string().describe("Its id from list_documents") }
38135
+ },
38136
+ async ({ document }) => {
38137
+ await space().removeDocument(document);
38138
+ return text({ removed: document });
38139
+ }
38140
+ );
37935
38141
  server.registerTool(
37936
38142
  "propose",
37937
38143
  {
@@ -37967,18 +38173,59 @@ server.registerTool(
37967
38173
  server.registerTool(
37968
38174
  "update_request",
37969
38175
  {
37970
- description: 'Report progress on a request that was addressed to you (a `request` event from wait_for_events whose `to` includes your agent id; its `seq` is the id). Everyone in the meeting sees the status next to the request, so keep it current: call with "acknowledged" the moment you receive the request, "working" with a short note on what you are doing before each slow step, and finish with "done" (note: what you did or where to look) or "failed" (note: why). A request left without a final status keeps showing as in progress.',
38176
+ description: 'Report progress on a request that was addressed to you (a `request` event from wait_for_events whose `to` includes your agent id; its `seq` is the id). Everyone in the meeting sees the status next to the request, and sees your figure in the room busy with it (several at once, if you are working on several), so keep it current: call with "acknowledged" the moment you receive the request, "working" with a short note on what you are doing before each slow step, and finish with "done" (note: what you did or where to look) or "failed" (note: why). A request left without a final status keeps showing as in progress.',
37971
38177
  inputSchema: {
37972
38178
  id: external_exports.number().int().describe("The request id: `seq` of the request event"),
37973
38179
  status: external_exports.enum(REQUEST_STATUSES),
37974
- note: external_exports.string().max(200).optional().describe("One short line shown next to the status")
38180
+ note: external_exports.string().max(200).optional().describe("One short line shown next to the status"),
38181
+ surfaceId: external_exports.string().optional().describe(
38182
+ "The surface this work is on, if any. You are shown on that tab while you work, so people see where to look. Pass it once you have created or picked the surface."
38183
+ ),
38184
+ doing: external_exports.enum(WORK_KINDS).optional().describe(DOING),
38185
+ documentId: WORK_DOCUMENT,
38186
+ section: WORK_SECTION
37975
38187
  }
37976
38188
  },
37977
- async ({ id, status, note }) => {
37978
- const entry = await space().updateRequest(id, status, note);
38189
+ async ({ id, status, note, surfaceId, doing, documentId, section }) => {
38190
+ const entry = await space().updateRequest(
38191
+ id,
38192
+ status,
38193
+ note,
38194
+ surfaceId,
38195
+ doing,
38196
+ documentId ? { docId: documentId, section } : void 0
38197
+ );
37979
38198
  return text({ request: entry.seq, status, to: entry.request?.to });
37980
38199
  }
37981
38200
  );
38201
+ server.registerTool(
38202
+ "working_on",
38203
+ {
38204
+ description: "Show everyone in the space what you are doing right now, when it is not an answer to a request (for a request, update_request takes the same `doing`). Your figure in the room plays the matching animation, on the surface's tab if you name one. Call it before any piece of work that takes more than a few seconds (your person asked you for something in your own conversation, say), again when what you are doing changes, and with `done: true` when you finish; it lapses by itself after three minutes. To show several things at once, give each its own `key`.",
38205
+ inputSchema: {
38206
+ doing: external_exports.enum(WORK_KINDS).optional().describe(DOING),
38207
+ note: external_exports.string().max(200).optional().describe("A few words on what it is"),
38208
+ surfaceId: external_exports.string().optional().describe("The surface it is happening on, if any"),
38209
+ documentId: WORK_DOCUMENT,
38210
+ section: WORK_SECTION,
38211
+ key: external_exports.string().max(40).optional().describe('Names this piece of work, when you have several at once (default "main")'),
38212
+ done: external_exports.boolean().optional().describe("Set true when this piece of work is finished")
38213
+ }
38214
+ },
38215
+ async ({ doing, note, surfaceId, documentId, section, key, done }) => {
38216
+ if (done) return text(await space().setWork({ key, done: true }));
38217
+ if (!doing) throw new Error(`working_on needs \`doing\`: one of ${WORK_KINDS.join(", ")}.`);
38218
+ return text(
38219
+ await space().setWork({
38220
+ key,
38221
+ doing,
38222
+ note,
38223
+ surfaceId,
38224
+ ...documentId ? { docId: documentId, section } : {}
38225
+ })
38226
+ );
38227
+ }
38228
+ );
37982
38229
  if (process.argv[2] === "setup") {
37983
38230
  process.exit(
37984
38231
  await setup(process.argv.slice(3)).catch((e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "symposium-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "MCP server for Symposium: lets an agent join a meeting space and put interactive UI on its shared stage.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",