bridgerator 1.1.10 → 1.1.11

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,12 +1,12 @@
1
1
  {
2
2
  "name": "bridgerator",
3
- "version": "1.1.10",
3
+ "version": "1.1.11",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "bridgerator",
9
- "version": "1.1.10",
9
+ "version": "1.1.11",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@modelcontextprotocol/sdk": "^1.29.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bridgerator",
3
- "version": "1.1.10",
3
+ "version": "1.1.11",
4
4
  "description": "Connect ChatGPT to your Windows PC: files, search and terminal through an outbound-only bridge with folder limits. No inbound ports, no admin rights.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/queue.mjs CHANGED
@@ -24,7 +24,7 @@ export const QUEUE_TOOL_DEFS = [
24
24
  { name: 'queue_list', description: `List the task queue the person keeps on this PC (a checklist of things to do): counts and items (id, title, status, note, worker, updatedAt, from, resultPath), optionally only one status.`, inputSchema: { type: 'object', additionalProperties: false, properties: { status: { type: 'string', enum: STATUSES } } } },
25
25
  { name: 'queue_next', description: `Take the next item of the task queue: returns the first "todo" item and marks it "doing" (nothing when the queue is empty). An item left "doing" for 30 minutes without an update (another chat stopped) is returned first, with resumed: true. Several chats can work through one queue at once; each call gets a different item. Pass worker = a short name for this chat (e.g. its title) so the person sees who is on it. ${RULE}`, inputSchema: { type: 'object', additionalProperties: false, properties: { worker: STR(80), id: { type: 'string', description: 'take this "todo" item instead of the first one' } } } },
26
26
  { name: 'queue_update', description: `Set an item's status (doing / done / failed / skipped / todo) with a short result note.`, inputSchema: { type: 'object', additionalProperties: false, required: ['id', 'status'], properties: { id: ID, status: { type: 'string', enum: STATUSES }, note: STR(MAX_NOTE) } } },
27
- { name: 'queue_add', description: `Add steps to the task queue (e.g. when planning a bigger job with the person): one { title, details?, paths?, from?, resultPath? } or items: [...] (1–100). Another AI handing work to ChatGPT sets from (its name) and resultPath (a file for the result), then reads the outcome with queue_list.`, inputSchema: { type: 'object', additionalProperties: false, properties: { title: STR(MAX_TITLE), details: STR(MAX_DETAILS), paths: PATHS, ...HANDOFF, items: { type: 'array', minItems: 1, maxItems: 100, items: { type: 'object', additionalProperties: false, required: ['title'], properties: { title: STR(MAX_TITLE), details: STR(MAX_DETAILS), paths: PATHS, ...HANDOFF } } } } } },
27
+ { name: 'queue_add', description: `Add steps to the task queue (e.g. when planning a bigger job with the person): one { title, details?, paths?, from?, resultPath? } or items: [...] (1–100). Another AI handing work to ChatGPT sets from (its name) and resultPath (a file for the result), then reads the outcome with queue_list. A step whose title matches an open (todo / doing) item is not added again — the reply names that item; allowDuplicate: true adds it anyway.`, inputSchema: { type: 'object', additionalProperties: false, properties: { title: STR(MAX_TITLE), details: STR(MAX_DETAILS), paths: PATHS, ...HANDOFF, allowDuplicate: { type: 'boolean' }, items: { type: 'array', minItems: 1, maxItems: 100, items: { type: 'object', additionalProperties: false, required: ['title'], properties: { title: STR(MAX_TITLE), details: STR(MAX_DETAILS), paths: PATHS, ...HANDOFF } } } } } },
28
28
  { name: 'queue_clear', description: 'Remove finished items from the task queue: status done, failed or skipped. status "all" removes every item and needs the person\'s OK on the PC.', inputSchema: { type: 'object', additionalProperties: false, properties: { status: { type: 'string', enum: ['done', 'failed', 'skipped', 'all'] } } } },
29
29
  { name: 'queue_edit', description: 'Change the title, details, paths or result file of a queue item.', inputSchema: { type: 'object', additionalProperties: false, required: ['id'], properties: { id: ID, title: STR(MAX_TITLE), details: STR(MAX_DETAILS), paths: PATHS, resultPath: HANDOFF.resultPath } } },
30
30
  { name: 'queue_reorder', description: 'Put queue items in a new order: ids first, in this order; the rest keep their order after them.', inputSchema: { type: 'object', additionalProperties: false, required: ['ids'], properties: { ids: { type: 'array', maxItems: MAX_ITEMS, items: ID } } } },
@@ -190,7 +190,18 @@ export function queueTool(q, name, args = {}) {
190
190
  case 'queue_list': return ok({ counts: q.counts(), items: q.list(args.status).map(({ id, title, status, note, worker, updatedAt, details, paths, from, resultPath }) => ({ id, title, status, ...(note ? { note } : {}), ...(worker ? { worker } : {}), updatedAt, ...(details ? { details } : {}), ...(paths ? { paths } : {}), ...(from ? { from } : {}), ...(resultPath ? { resultPath } : {}) })) });
191
191
  case 'queue_next': { const it = q.next(args); return ok(it ? { item: it } : { item: null, message: 'The queue is empty — tell the person and stop.' }); }
192
192
  case 'queue_update': return ok({ item: q.update(args) });
193
- case 'queue_add': return args.items ? ok({ items: q.addMany(args.items) }) : ok({ item: q.add(args) });
193
+ case 'queue_add': {
194
+ // A second chat given the same request re-added the whole set (E2E 26.10): an open item with the same
195
+ // title is skipped and named. Only the AI's tool; the window adds what the person types.
196
+ const key = (t) => (typeof t === 'string' ? t.trim().toLowerCase() : null);
197
+ const open = new Map(q.list().filter((x) => x.status === 'todo' || x.status === 'doing').map((x) => [key(x.title), x.id]));
198
+ const want = Array.isArray(args.items) ? args.items : [args], keep = [], skipped = [];
199
+ for (const x of want) { const d = args.allowDuplicate === true ? undefined : open.get(key(x?.title)); if (d) skipped.push({ title: x.title, duplicate_of: d }); else keep.push(x); }
200
+ const why = skipped.length ? { skipped, message: 'Already in the queue (open), not added again: work on those items (queue_list / queue_next), or pass allowDuplicate: true to add them anyway.' } : {};
201
+ if (!keep.length && skipped.length) return ok({ added: 0, ...why });
202
+ if (args.items === undefined) return ok({ item: q.add(args) });
203
+ return ok({ items: q.addMany(Array.isArray(args.items) ? keep : args.items), ...why });
204
+ }
194
205
  case 'queue_clear': { if (args.status === 'all') throw new Error('clearing every item needs the person\'s OK'); const gone = q.clear({ status: args.status || 'finished' }); return ok({ removed: gone.length, counts: q.counts() }); }
195
206
  case 'queue_edit': return ok({ item: q.edit(args) });
196
207
  case 'queue_reorder': return ok({ items: q.reorder(args) });