@patimweb/pi-teams 1.9.0 → 1.9.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/package.json +1 -1
  2. package/src/watch/loop.ts +33 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patimweb/pi-teams",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
4
4
  "description": "Microsoft Teams integration for pi coding agent — acts fully as the signed-in user across multiple accounts and tenants: chats, channels, meetings, presence, with configurable guardrails",
5
5
  "keywords": [
6
6
  "pi-package",
package/src/watch/loop.ts CHANGED
@@ -97,6 +97,18 @@ export interface WatchRuntimeStatus {
97
97
  // One tick
98
98
  // ---------------------------------------------------------------------------
99
99
 
100
+ /** A tick was asked to run without knowing who pi is signed in as. */
101
+ export class WatchIdentityError extends Error {
102
+ constructor(cause?: unknown) {
103
+ super(
104
+ "Listen mode is waiting: the signed-in user could not be determined, so pi's own replies " +
105
+ "could not be told apart from new messages. Retrying on the next poll." +
106
+ (cause ? ` ${formatGraphError(cause)}` : ""),
107
+ );
108
+ this.name = "WatchIdentityError";
109
+ }
110
+ }
111
+
100
112
  /**
101
113
  * Bound on how many chats one tick examines.
102
114
  *
@@ -165,8 +177,14 @@ export async function runWatchTick(
165
177
  deps: WatchTickDeps,
166
178
  state: WatchState,
167
179
  watch: ResolvedWatchConfig,
168
- me: SignedInUser | undefined,
180
+ me: SignedInUser,
169
181
  ): Promise<WatchTickResult> {
182
+ // Who pi acts as is what tells its own replies apart from everybody else's
183
+ // (`isFromMe`). Without it, the answer pi just sent looks like a new message
184
+ // from a colleague and wakes pi again — a loop that answers itself. So a tick
185
+ // without an identity examines nothing, and in particular writes no cursor.
186
+ if (!me) throw new WatchIdentityError();
187
+
170
188
  const now = deps.now();
171
189
  pruneState(state, now);
172
190
 
@@ -337,8 +355,15 @@ export function startWatchLoop(options: WatchLoopOptions): WatchLoop {
337
355
  * instead of starting a second one.
338
356
  */
339
357
  let inFlight: Promise<void> | undefined;
358
+ /**
359
+ * Who pi acts as, looked up on the first tick that can reach Graph.
360
+ *
361
+ * A failed lookup is not an answer: it is retried on every tick until it
362
+ * succeeds, and no tick runs before it has. A watcher started before sign-in
363
+ * (autoStart in a fresh container, say) would otherwise settle for "nobody"
364
+ * for the whole session and wake on its own replies.
365
+ */
340
366
  let me: SignedInUser | undefined;
341
- let meResolved = false;
342
367
  let failureCount = 0;
343
368
 
344
369
  const status: WatchRuntimeStatus = {
@@ -361,9 +386,12 @@ export function startWatchLoop(options: WatchLoopOptions): WatchLoop {
361
386
  if (stopped) return;
362
387
 
363
388
  try {
364
- if (!meResolved) {
365
- me = await getMe(connection).catch(() => undefined);
366
- meResolved = true;
389
+ if (!me) {
390
+ try {
391
+ me = await getMe(connection);
392
+ } catch (err) {
393
+ throw new WatchIdentityError(err);
394
+ }
367
395
  }
368
396
 
369
397
  const result = await runWatchTick(