@volter/teams 0.1.50 → 0.1.52

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.
@@ -54,7 +54,7 @@ export function sendBrowserTurn(channel: BrowserChannel, text: string, options?:
54
54
 
55
55
  export interface MailHistoryEntry {
56
56
  id: string; from: string; fromName: string; to: string; text: string; at: number;
57
- direction: 'in' | 'out'; inReplyTo: string | null; delivery: string;
57
+ subject?: string | null; direction: 'in' | 'out'; inReplyTo: string | null; delivery: string;
58
58
  fromActor?: MailActor; toActor?: MailActor;
59
59
  }
60
60
  /** The existing native identity resolved by supercode, not the return mailbox. */
package/machine-core.mjs CHANGED
@@ -244,7 +244,7 @@ export class MachineCore extends EventEmitter {
244
244
  }
245
245
  const index = this.nextConnection++;
246
246
  // A delegated caller's claim also carries the name its workplace signed for it (who typed a turn).
247
- const entry = { channel, admitted, principal: claim.principal, name: typeof claim.name === 'string' ? claim.name : null, verify: () => this.#admitClaim(claim), localAdmission: () => admitFromGrants(claim, { custodianId: this.custodianId, grants: this.linkGrants }) };
247
+ const entry = { channel, admitted, principal: claim.principal, name: typeof claim.name === 'string' ? claim.name : null, roomContext: claim.roomContext ?? null, verify: () => this.#admitClaim(claim), localAdmission: () => admitFromGrants(claim, { custodianId: this.custodianId, grants: this.linkGrants }) };
248
248
  this.connections.set(index, entry);
249
249
  stream.on('close', () => { this.connections.delete(index); this.connectionClosed(index); });
250
250
  this.log.append({ principal: entry.principal, cap: 'connect', target: '*', outcome: admitted.class, detail: 'server' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/teams",
3
- "version": "0.1.50",
3
+ "version": "0.1.52",
4
4
  "type": "module",
5
5
  "description": "Volter Teams, the neutral team plane: machine identity and daemon core, the local door and server link, admission from machine grants, product doors, and the Teams server's neutral routes (company RFC 0008).",
6
6
  "license": "MIT",
@@ -102,7 +102,8 @@ export async function delegatedSessionOf(api, token) {
102
102
  // The member's name as the workplace knows it: who the session is told typed a turn. From the signed token, never
103
103
  // from what the browser sends.
104
104
  const name = typeof claims.name === 'string' && claims.name.trim() ? claims.name.trim().slice(0, 80) : null;
105
- return { conversation: claims.conversation, expiresAt: claims.exp * 1000, issuer: claims.iss, name, room: claims.room, scope: claims.scope, sessionRef: session.id, subject: claims.sub, teamId: session.team };
105
+ const roomContext = claims.roomContext === false ? null : { id: claims.room, name: typeof claims.roomName === 'string' ? claims.roomName.slice(0, 120) : claims.room, sender: claims.sub };
106
+ return { roomContext, conversation: claims.conversation, expiresAt: claims.exp * 1000, issuer: claims.iss, name, room: claims.room, scope: claims.scope, sessionRef: session.id, subject: claims.sub, teamId: session.team };
106
107
  }
107
108
 
108
109
  /** A token's claims when it is one this server trusts: signed by the issuer's key, for this server, in its lifetime. */
@@ -56,7 +56,7 @@ export async function machineGrantRows(api, teamId, machineId) {
56
56
  async function claimFor(api, auth, machineId) {
57
57
  if (auth.kind === 'delegated') {
58
58
  const row = await api.store.one('SELECT id, revision FROM machine_grants WHERE team_id=? AND machine_id=? AND id=? AND expires_at>?', [auth.team_id, machineId, auth.grant_id, api.clock.nowMs()]);
59
- return row ? { principal: auth.principal_id, groups: [], grants: [row.id], revisions: { [row.id]: row.revision }, ...(auth.name ? { name: auth.name } : {}) } : null;
59
+ return row ? { principal: auth.principal_id, groups: [], grants: [row.id], revisions: { [row.id]: row.revision }, ...(auth.name ? { name: auth.name } : {}), ...(auth.roomContext ? { roomContext: auth.roomContext } : {}) } : null;
60
60
  }
61
61
  const machine = await api.store.one('SELECT custodian_id FROM machines WHERE team_id=? AND id=? AND revoked_at IS NULL', [auth.team_id, machineId]);
62
62
  if (!machine) return null;
@@ -231,7 +231,7 @@ async function delegatedSessionChannel(api, teamId, sessionRef, token) {
231
231
  { sql: "DELETE FROM machine_grants WHERE team_id=? AND issuer_id LIKE 'delegated:%' AND expires_at<=?", params: [teamId, now] },
232
232
  { sql: 'INSERT INTO machine_grants(team_id,id,machine_id,subject_id,caps_json,targets_json,issuer_id,created_at,expires_at) VALUES (?,?,?,?,?,?,?,?,?)', params: [teamId, id, target.machineId, principal, JSON.stringify(access.scope === 'control' ? ['observe', 'interact'] : ['observe']), JSON.stringify([`session:${target.harness}/${target.nativeId}`]), issuer, now, access.expiresAt] },
233
233
  ]).catch((error) => { if (error.code !== 'not_found') throw error; return null; });
234
- return { door: 'channel', teamId, machineId: target.machineId, auth: { kind: 'delegated', team_id: teamId, principal_id: principal, group_ids: [], grant_id: id, grant_issuer: issuer, expires_at: access.expiresAt, name: access.name } };
234
+ return { door: 'channel', teamId, machineId: target.machineId, auth: { kind: 'delegated', team_id: teamId, principal_id: principal, group_ids: [], grant_id: id, grant_issuer: issuer, expires_at: access.expiresAt, name: access.name, roomContext: access.roomContext } };
235
235
  }
236
236
 
237
237
  /**
@@ -151,7 +151,7 @@ export function withMail(transcript, rows, { agentName = 'This session', agentId
151
151
  const mail = rows.map(row => ({ id: `mail:${row.id}`, role: 'notice', text: row.text, ts: row.at, truncated: false,
152
152
  mail: { id: row.id, from: nameOf(row.fromActor), to: row.toActor ? nameOf(row.toActor) : row.direction === 'in' ? agentName : 'Another session',
153
153
  image: row.fromActor?.kind === 'agent' ? picture(row.fromActor.self ? agentId : row.fromActor.id) : `https://brand.volter.ai/bot/svg?seed=${encodeURIComponent(row.fromActor?.id || row.from)}&size=24&radius=4`,
154
- via: row.fromActor?.via, direction: row.direction, inReplyTo: row.inReplyTo } }));
154
+ subject: row.subject || undefined, via: row.fromActor?.via, direction: row.direction, inReplyTo: row.inReplyTo } }));
155
155
  const result = transcript.filter(entry => !entry.mail || !rows.some(row => row.id === entry.mail.id || row.id.startsWith(entry.mail.id)));
156
156
  // Keep native tool/result ordering intact (some native entries have no time).
157
157
  for (const entry of mail.sort((a, b) => a.ts - b.ts)) {