@evandu/super-pi 0.1.164 → 0.1.166

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 (48) hide show
  1. package/agents/coder.md +13 -1
  2. package/agents/critic.md +6 -2
  3. package/agents/patcher.md +6 -2
  4. package/agents/planner.md +14 -2
  5. package/agents/project-leader.md +25 -0
  6. package/agents/reviewer.md +13 -1
  7. package/agents/scout.md +6 -2
  8. package/agents/super-pi-task/core.ts +11 -7
  9. package/agents/verifier.md +6 -2
  10. package/dist/client/.super-pi-client-sources.json +10 -8
  11. package/dist/client/assets/{CodeMirrorEditor-B7hhyG33.js → CodeMirrorEditor-q5ckKYpW.js} +2 -2
  12. package/dist/client/assets/{dist-CdpuBRoy.js → dist-3Qp_-Z3t.js} +1 -1
  13. package/dist/client/assets/{dist-B5xG-jTK.js → dist-B61EUjUz.js} +1 -1
  14. package/dist/client/assets/{dist-BLhPsVFt.js → dist-BN9HZG4O.js} +1 -1
  15. package/dist/client/assets/{dist-CxZb6ATc.js → dist-BOU0i9JW.js} +1 -1
  16. package/dist/client/assets/{dist-BBfDBMAk.js → dist-BT3fZ0wT.js} +1 -1
  17. package/dist/client/assets/{dist-BDy5Nw4_.js → dist-B_l0KBjV.js} +1 -1
  18. package/dist/client/assets/{dist-Foaz_oK2.js → dist-Bb-gT7aj.js} +1 -1
  19. package/dist/client/assets/{dist-D607J8oC.js → dist-CBqBGuBl.js} +1 -1
  20. package/dist/client/assets/{dist-CGO5Bn1x.js → dist-CXXAxAE4.js} +1 -1
  21. package/dist/client/assets/{dist-DJ4sWDSy.js → dist-Crew6BC1.js} +1 -1
  22. package/dist/client/assets/{dist-BjlmPyRl.js → dist-Cs1P7JrO.js} +1 -1
  23. package/dist/client/assets/{dist-CsBiEr54.js → dist-CyTiEqGG.js} +1 -1
  24. package/dist/client/assets/{dist-7Nz91m3a.js → dist-D10kcMrw.js} +1 -1
  25. package/dist/client/assets/{dist-D6nsZZd3.js → dist-DMynScoj.js} +1 -1
  26. package/dist/client/assets/{dist-CrJV_eES.js → dist-DRHWMjx_.js} +1 -1
  27. package/dist/client/assets/{dist-qqDS87aN.js → dist-DSIy9Ers.js} +1 -1
  28. package/dist/client/assets/{dist-BmeBkDNB.js → dist-DdfZJR8e.js} +1 -1
  29. package/dist/client/assets/{dist-CF9zuubZ.js → dist-DiqGp2ie.js} +1 -1
  30. package/dist/client/assets/{dist-ByEzPyEi.js → dist-YFQ3ixZU.js} +1 -1
  31. package/dist/client/assets/{dist-DaUyp_NN.js → dist-_pAt4bRw.js} +1 -1
  32. package/dist/client/assets/{dist-BhouAfNI.js → dist-bLHDT2q5.js} +1 -1
  33. package/dist/client/assets/{dist-C4ql8ak8.js → dist-dbLsO_41.js} +1 -1
  34. package/dist/client/assets/{index-Wtlj0s4R.css → index-C6YWfUu2.css} +1 -1
  35. package/dist/client/assets/{index-1hS3wVqF.js → index-dARLbQ-8.js} +19 -19
  36. package/dist/client/index.html +2 -2
  37. package/dist/server/agents.d.ts +11 -0
  38. package/dist/server/agents.js +50 -6
  39. package/dist/server/application/project-service.d.ts +95 -4
  40. package/dist/server/application/project-service.js +328 -72
  41. package/dist/server/http-server.js +7 -0
  42. package/dist/server/store.d.ts +17 -0
  43. package/dist/server/store.js +97 -1
  44. package/dist/server/subagent-runs.js +19 -1
  45. package/dist/server/task-tool.d.ts +1 -1
  46. package/dist/server/task-tool.js +4 -3
  47. package/dist/server/types.d.ts +44 -1
  48. package/package.json +11 -2
@@ -8,8 +8,8 @@
8
8
  <link rel="alternate icon" href="/favicon.ico" />
9
9
  <link rel="apple-touch-icon" href="/favicon.svg" />
10
10
  <title>Super Pi</title>
11
- <script type="module" crossorigin src="/assets/index-1hS3wVqF.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-Wtlj0s4R.css">
11
+ <script type="module" crossorigin src="/assets/index-dARLbQ-8.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-C6YWfUu2.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -175,6 +175,16 @@ export interface ResolveAgentDirectoriesOptions {
175
175
  env?: NodeJS.ProcessEnv;
176
176
  rootDir?: string;
177
177
  }
178
+ /**
179
+ * The agents directory that ships WITH the package: the definitions Super Pi itself carries.
180
+ *
181
+ * WHY IT IS A FUNCTION AND NOT A CONSTANT. The package root is not the same directory in every
182
+ * layout - it is the repository root in a checkout and the installed package root in an npm install
183
+ * - so the path is derived from the same `rootDir` the discovery uses, and both sides of the
184
+ * "is this built-in?" question ask THIS function. If the two ever disagreed, a user agent could be
185
+ * filed as built-in and a built-in one as the user's, and the picker would sort them wrongly.
186
+ */
187
+ export declare function builtinAgentsDir(rootDir?: string): string;
178
188
  /**
179
189
  * Resolves the ordered list of directories to discover agents from:
180
190
  * 1. Custom directories configured by the user in app settings (or options)
@@ -187,6 +197,7 @@ export interface ResolveAgentDirectoriesOptions {
187
197
  export declare function resolveAgentDirectories(options?: ResolveAgentDirectoriesOptions): string[];
188
198
  export declare function readAgents(directory: string | readonly string[], options?: {
189
199
  mode?: AgentModeFilter;
200
+ builtinDir?: string;
190
201
  }): Promise<AgentScan>;
191
202
  /** Parses one definition file, or returns the reason it is not a usable agent. */
192
203
  export declare function parseDefinition(file: string, source: string): AgentDefinition | AgentProblem;
@@ -392,6 +392,18 @@ export function expandTilde(filepath) {
392
392
  }
393
393
  return filepath;
394
394
  }
395
+ /**
396
+ * The agents directory that ships WITH the package: the definitions Super Pi itself carries.
397
+ *
398
+ * WHY IT IS A FUNCTION AND NOT A CONSTANT. The package root is not the same directory in every
399
+ * layout - it is the repository root in a checkout and the installed package root in an npm install
400
+ * - so the path is derived from the same `rootDir` the discovery uses, and both sides of the
401
+ * "is this built-in?" question ask THIS function. If the two ever disagreed, a user agent could be
402
+ * filed as built-in and a built-in one as the user's, and the picker would sort them wrongly.
403
+ */
404
+ export function builtinAgentsDir(rootDir) {
405
+ return resolve(join(rootDir ?? resolve(import.meta.dirname, "../.."), "agents"));
406
+ }
395
407
  /**
396
408
  * Resolves the ordered list of directories to discover agents from:
397
409
  * 1. Custom directories configured by the user in app settings (or options)
@@ -433,11 +445,25 @@ export function resolveAgentDirectories(options = {}) {
433
445
  catch {
434
446
  // homedir() might throw in restricted sandbox environments
435
447
  }
436
- const root = options.rootDir ?? resolve(import.meta.dirname, "../..");
437
- add(join(root, "agents"));
448
+ add(builtinAgentsDir(options.rootDir));
438
449
  }
439
450
  return dirs;
440
451
  }
452
+ /**
453
+ * Whether two paths name the same directory.
454
+ *
455
+ * WHY THE COMPARISON IS NOT A PLAIN `===`. The paths that reach here were built by joining and
456
+ * resolving, so they are already absolute - but Windows paths are case-insensitive and a directory
457
+ * typed by hand (`d:\agents`) is the same directory as the one the app resolved itself
458
+ * (`D:\Agents`). Comparing them as strings would call the package's own agents directory "the user's"
459
+ * and put the built-in agents at the bottom of the picker, which is the defect this whole change is
460
+ * about.
461
+ */
462
+ function sameDirectory(left, right) {
463
+ const a = resolve(left);
464
+ const b = resolve(right);
465
+ return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
466
+ }
441
467
  export async function readAgents(directory, options = {}) {
442
468
  const mode = options.mode ?? "primary";
443
469
  const rawDirs = Array.isArray(directory) ? directory : [directory];
@@ -445,6 +471,9 @@ export async function readAgents(directory, options = {}) {
445
471
  const agents = [];
446
472
  const problems = [];
447
473
  const seenIds = new Set();
474
+ // The directory whose definitions are the PACKAGE'S OWN. Everything else is the user's, and the
475
+ // two are listed separately so the package's agents can come first (see the sort at the end).
476
+ const builtinDir = options.builtinDir ?? builtinAgentsDir();
448
477
  for (const dir of dirs) {
449
478
  let entries;
450
479
  try {
@@ -455,6 +484,7 @@ export async function readAgents(directory, options = {}) {
455
484
  continue;
456
485
  throw error;
457
486
  }
487
+ const isBuiltin = sameDirectory(dir, builtinDir);
458
488
  for (const file of entries.filter((name) => name.toLowerCase().endsWith(".md")).sort()) {
459
489
  // A FILE THAT CANNOT BE READ IS THE SAME KIND OF PROBLEM AS ONE THAT CANNOT BE PARSED.
460
490
  //
@@ -487,17 +517,31 @@ export async function readAgents(directory, options = {}) {
487
517
  else {
488
518
  if (!seenIds.has(parsed.id)) {
489
519
  seenIds.add(parsed.id);
490
- agents.push({ ...parsed, sourceDir: dir });
520
+ agents.push({ ...parsed, sourceDir: dir, builtin: isBuiltin });
491
521
  }
492
522
  }
493
523
  }
494
524
  }
495
525
  // The half that was NOT asked for is filtered out here rather than marked in the UI: sub-agents are
496
526
  // invoked BY an agent and never chosen by a human, so listing one in the picker would promise a role
497
- // the runtime cannot honour. The remainder is alphabetical by id, so either roster reads as a list
498
- // rather than as a directory listing.
527
+ // the runtime cannot honour.
528
+ //
529
+ // The remainder is the PACKAGE'S OWN agents first, then the user's, each group alphabetical by id.
530
+ //
531
+ // WHY THE BUILT-IN GROUP COMES FIRST. The picker used to sort purely by id, which scattered the
532
+ // package's own agents - the ones drawn with a colour dot - among the user's own with no visible
533
+ // rule to say which was which. The built-in group is now a group, so the colours read as "these are
534
+ // the ones that ship with Super Pi" instead of as noise.
535
+ //
536
+ // WHY THE GROUP ORDER DOES NOT TOUCH THE OVERRIDE RULE. The rule that decides WHICH definition of
537
+ // an id survives was applied above, while reading: the first directory to offer an id wins, and the
538
+ // user's directories are read before the package's own. Sorting here only reorders definitions that
539
+ // already survived that decision, so a user's copy of a built-in agent still wins - and it is then
540
+ // listed as the user's, because it IS the user's file.
499
541
  return {
500
- agents: agents.filter((agent) => agent.mode === mode).sort((a, b) => a.id.localeCompare(b.id)),
542
+ agents: agents
543
+ .filter((agent) => agent.mode === mode)
544
+ .sort((a, b) => (a.builtin === b.builtin ? a.id.localeCompare(b.id) : a.builtin ? -1 : 1)),
501
545
  problems,
502
546
  };
503
547
  }
@@ -1,8 +1,39 @@
1
1
  import type { Store } from "../store.js";
2
2
  import type { Project, ProjectEscalation, ProjectMode, ProjectReport, ProjectRisk, ProjectSummary, ProjectTask, SessionProfile } from "../types.js";
3
3
  import type { SessionOrchestratorService } from "./session-orchestrator-service.js";
4
+ /**
5
+ * WHAT THE PROJECT LEADER IS TOLD WHEN IT STARTS.
6
+ *
7
+ * The two prompts are kept apart on purpose. The standard one is the shape the project flow has
8
+ * always had - decompose, delegate, review. The lightweight one is a different set of
9
+ * instructions about a different shape: one session, no decomposition, no reviewer. Writing one
10
+ * prompt with holes in it would mean every sentence had to carry a condition, and the model would
11
+ * have to work out which shape it was in.
12
+ *
13
+ * THE ESCALATION RULE IS SAID ONCE, IN ONE VOICE. The standard leader used to be told two
14
+ * opposite things about the same event - "your subordinates' escalations come to you first, you
15
+ * decide whether to handle them yourself or report them up" AND "you must not resolve escalations
16
+ * yourself" - and a model reading both has no way to pick, so it hesitates between them. The
17
+ * rule is now one sentence with three parts: handle it at your own level, escalate only what
18
+ * really needs the Boss, and never decide for the Boss or drop an escalation. The lightweight
19
+ * leader has no subordinates at all, so it is told the second half only - it must not decide
20
+ * alone either - and is never told about subordinates it does not have.
21
+ */
22
+ export declare const STANDARD_LEADER_PROMPT = "\u4F60\u662F\u76F4\u63A5\u5411 Boss \u8D1F\u8D23\u7684\u9879\u76EE Leader\uFF0C\u517C\u4EFB\u79D8\u4E66\u3002\u5148\u8C03\u7528 project(action=get, projectId) \u4E86\u89E3\u6700\u65B0\u72B6\u6001\u3002\u5C0F\u9879\u76EE\u53EA\u62C6\u6700\u5C11\u5FC5\u8981\u4EFB\u52A1\uFF0C\u5927\u9879\u76EE\u6309\u4EA4\u4ED8\u8FB9\u754C\u5206\u5DE5\u3002\u7528 add_task \u767B\u8BB0\u4EFB\u52A1\uFF08\u660E\u786E\u76EE\u6807\u3001\u4EA4\u4ED8\u7269\u3001\u9A8C\u6536\u6807\u51C6\u3001\u4F9D\u8D56\uFF09\uFF1Badd_task / update_task \u65F6\u53EF\u4EE5\u5E26 executorProfile \u4E0E reviewerProfile\uFF08\u652F\u6301\u6307\u5B9A agentId\u3001cwd\u3001model\u3001thinkingLevel\u3001watchdogTimeoutSeconds\uFF09\uFF0C\u6309\u4EFB\u52A1\u6027\u8D28\u5C06\u6267\u884C\u4E0E\u590D\u6838\u59D4\u6D3E\u7ED9\u6700\u5408\u9002\u7684\u5458\u5DE5\uFF0C\u4E0D\u6307\u5B9A\u5219\u7EE7\u627F\u9879\u76EE\u9ED8\u8BA4\uFF1B\u9700\u8981\u7EE7\u7EED\u62C6\u89E3\u7684\u5DE5\u4F5C\u8BBE role=leader\uFF0C\u53F6\u5B50\u6267\u884C\u4EFB\u52A1\u8BBE role=executor\uFF0C\u5B50 Leader \u7528 parentTaskId \u767B\u8BB0\u81EA\u5DF1\u7684\u5B50\u4EFB\u52A1\uFF0Cdispatch \u542F\u52A8\u6267\u884C\u5B50\u4F1A\u8BDD\uFF1B\u4E0D\u8981\u7528 session_spawn/session_dispatch \u6216 task/workflow \u7ED5\u8FC7\u9879\u76EE\u8D44\u6E90\u9650\u5236\u3002\u6536\u5230\u4EA4\u4ED8\u540E\u7528 request_review \u542F\u52A8\u72EC\u7ACB\u590D\u6838\u3002\u5B50\u4F1A\u8BDD\u7ED3\u675F\u4F1A\u81EA\u52A8\u56DE\u62A5\u7ED9\u4F60\uFF0C\u5C4A\u65F6\u7EE7\u7EED\u6D3E\u53D1 ready \u4EFB\u52A1\u3001\u590D\u6838\u5F85\u9A8C\u6536\u4EFB\u52A1\u6216\u6309 review.summary \u8FD4\u5DE5\u3002\u4E0D\u8981\u8F6E\u8BE2\u7B49\u5F85\uFF0C\u4E0D\u8981\u53CD\u590D\u65E0\u8FDB\u5C55\u91CD\u8BD5\uFF0C\u5E76\u53D1\u6EE1\u65F6\u7ED3\u675F\u5F53\u524D\u56DE\u590D\u7B49\u5F85\u56DE\u62A5\u3002\u7528 report \u767B\u8BB0\u5173\u952E\u8FDB\u5C55\u3002\u4F60\u7684\u4E0B\u5C5E\u7684\u5347\u7EA7\u5148\u5230\u4F60\u8FD9\u91CC\uFF1A\u4F60\u5728\u81EA\u5DF1\u8FD9\u4E00\u5C42\u5904\u7406\uFF0C\u80FD\u81EA\u5DF1\u89E3\u51B3\u7684\u81EA\u5DF1\u89E3\u51B3\uFF1B\u53EA\u6709\u786E\u5B9E\u9700\u8981 Boss \u51B3\u7B56\u7684\uFF08\u91CD\u5927\u504F\u5DEE\u3001\u6743\u9650\u6216\u9884\u7B97\u95EE\u9898\uFF09\u624D\u7528 escalate \u4EA4\u7ED9 Boss\u3002\u4F60\u4E0D\u80FD\u66FF Boss \u505A\u51B3\u5B9A\uFF0C\u4E5F\u4E0D\u8981\u628A\u6CA1\u5904\u7406\u7684\u5347\u7EA7\u538B\u4E0B\u53BB\u4E0D\u7406\u3002\u6240\u6709\u4EFB\u52A1\u9A8C\u6536\u540E\uFF0C\u7528 complete \u63D0\u4EA4\u5E26\u8BC1\u636E\u7684\u6700\u7EC8\u4EA4\u4ED8\u603B\u7ED3\u3002\u4E0A\u4E0B\u6587\u6709\u9650\uFF0C\u660E\u786E\u5047\u8BBE\uFF0C\u4E3B\u52A8\u5BFB\u627E\u53CD\u8BC1\u3002\u53EA\u5728\u672C\u6B21\u89E6\u53D1\u8303\u56F4\u5185\u5DE5\u4F5C\u3002";
23
+ export declare const LIGHTWEIGHT_LEADER_PROMPT = "\u4F60\u662F\u76F4\u63A5\u5411 Boss \u8D1F\u8D23\u7684\u9879\u76EE Leader\uFF0C\u8FD9\u662F\u4E00\u4E2A\u8F7B\u91CF\u9879\u76EE\uFF1A\u8FD9\u4E2A\u9879\u76EE\u7531\u4F60\u4EB2\u81EA\u5B8C\u6210\uFF0C\u4E0D\u8981\u62C6\u5B50\u4EFB\u52A1\u3001\u4E0D\u8981\u6D3E\u53D1\u7ED9\u522B\u4EBA\u3001\u4E5F\u6CA1\u6709\u72EC\u7ACB\u590D\u6838\u3002\u5148\u8C03\u7528 project(action=get, projectId) \u4E86\u89E3\u6700\u65B0\u72B6\u6001\u3002\u7528 add_task \u767B\u8BB0\u4EFB\u52A1\uFF08\u660E\u786E\u76EE\u6807\u3001\u4EA4\u4ED8\u7269\u3001\u9A8C\u6536\u6807\u51C6\u3001\u4F9D\u8D56\uFF09\uFF0C\u4EFB\u52A1\u4E00\u5F8B\u767B\u8BB0\u4E3A\u6267\u884C\u4EFB\u52A1\uFF1Arole=leader \u6216\u5E26 parentTaskId \u90FD\u4F1A\u88AB\u62D2\u7EDD\u3002dispatch \u4F1A\u628A\u4EFB\u52A1\u4EA4\u56DE\u7ED9\u4F60\u81EA\u5DF1\uFF0C\u4F60\u4EB2\u81EA\u505A\u5B8C\uFF1B\u505A\u5B8C\u540E\u8C03\u7528 project(action=submit, projectId, taskId, output, evidenceRefs) \u63D0\u4EA4\u4EA4\u4ED8\uFF0C\u63D0\u4EA4\u5373\u5B8C\u6210\uFF0C\u4E0D\u4F1A\u6709\u4EBA\u56DE\u6765\u590D\u6838\uFF0C\u8BC1\u636E\u5FC5\u987B\u9644\u4E0A\u3002\u4E0D\u8981\u7528 session_spawn/session_dispatch \u6216 task/workflow \u7ED5\u8FC7\u9879\u76EE\u8D44\u6E90\u9650\u5236\u3002\u4E0D\u8981\u8F6E\u8BE2\u7B49\u5F85\uFF0C\u4E0D\u8981\u53CD\u590D\u65E0\u8FDB\u5C55\u91CD\u8BD5\u3002\u7528 report \u767B\u8BB0\u5173\u952E\u8FDB\u5C55\uFF0C\u7528 escalate \u5C06\u91CD\u5927\u504F\u5DEE\u3001\u6743\u9650\u6216\u9884\u7B97\u95EE\u9898\u4EA4\u7ED9 Boss\uFF0C\u4E0D\u5F97\u81EA\u884C\u89E3\u51B3\u5347\u7EA7\u4E8B\u9879\u3002\u6240\u6709\u4EFB\u52A1\u4EA4\u4ED8\u540E\uFF0C\u7528 complete \u63D0\u4EA4\u5E26\u8BC1\u636E\u7684\u6700\u7EC8\u4EA4\u4ED8\u603B\u7ED3\u3002\u4E0A\u4E0B\u6587\u6709\u9650\uFF0C\u660E\u786E\u5047\u8BBE\uFF0C\u4E3B\u52A8\u5BFB\u627E\u53CD\u8BC1\u3002\u53EA\u5728\u672C\u6B21\u89E6\u53D1\u8303\u56F4\u5185\u5DE5\u4F5C\u3002";
4
24
  export declare const profile: (value: unknown, label: string) => SessionProfile | undefined;
5
25
  export declare const mergeProfile: (taskProfile?: SessionProfile, projectDefault?: SessionProfile) => SessionProfile | undefined;
26
+ /**
27
+ * THE PROJECTS ONE LIST CALL ANSWERS WITH, AND HOW MANY THERE REALLY ARE.
28
+ *
29
+ * `projects` is the capped list, newest update first. `total` is how many projects the session
30
+ * is related to, counted BEFORE the cap, and `returned` is how many of them are in the list.
31
+ */
32
+ export type ProjectListPage = {
33
+ projects: ProjectSummary[];
34
+ total: number;
35
+ returned: number;
36
+ };
6
37
  export type CreateProjectInput = {
7
38
  workspaceId: string;
8
39
  title: string;
@@ -56,6 +87,7 @@ export declare class ProjectService {
56
87
  private readonly queues;
57
88
  constructor(store: Store, orchestrator?: (Pick<SessionOrchestratorService, "spawn" | "dispatch"> & Partial<Pick<SessionOrchestratorService, "stopSubtree">>) | undefined);
58
89
  static taskView(task: ProjectTask, project: Pick<Project, "defaultExecutorProfile" | "defaultReviewerProfile">): ProjectTask;
90
+ private filterEscalations;
59
91
  private projectView;
60
92
  /**
61
93
  * THE NUMBER THE ONE-CLICK PATH OFFERS.
@@ -72,6 +104,25 @@ export declare class ProjectService {
72
104
  private event;
73
105
  private change;
74
106
  private live;
107
+ /**
108
+ * WAKING THE OTHER SIDE MUST NOT TAKE THE REPORT DOWN WITH IT.
109
+ *
110
+ * The report, the escalation or the decision is already written to the store by the time the
111
+ * other side is woken, so a dispatch that fails - a session that was archived, stopped or never
112
+ * existed - must NOT fail the request. It would: the caller would be told the whole thing
113
+ * failed although it succeeded, it would try again, and the same report would be written twice.
114
+ *
115
+ * So the wake is wrapped, the failure is written down, and the record is returned as the
116
+ * success it is.
117
+ *
118
+ * AND THE FAILURE IS NOT SILENT. A report nobody was told about is a report nobody will ever
119
+ * handle, so the project's own timeline gets an event saying exactly that, and the log line
120
+ * names the project and the escalation so the case can be found again later.
121
+ */
122
+ private wake;
123
+ /** THE TIMELINE SAYS THAT NOBODY COULD BE TOLD. Written after the fact, so it never loses the
124
+ * record that was already saved. */
125
+ private noteUnreachable;
75
126
  private leader;
76
127
  private topLevelAuthor;
77
128
  private manager;
@@ -92,14 +143,54 @@ export declare class ProjectService {
92
143
  private requiredComplete;
93
144
  private task;
94
145
  private member;
146
+ isMember(p: Project, actor?: string): boolean;
147
+ directSupervisor(p: Project, actor?: string, taskId?: string): string | undefined;
148
+ /**
149
+ * THE PROJECT A SESSION BELONGS TO RIGHT NOW.
150
+ *
151
+ * `projectForSession` answers a different question - "which project is this session part of" -
152
+ * and it counts the finished projects too, because the history of a project has to stay visible
153
+ * to the people who worked on it. This method answers the narrower question the QUESTION path
154
+ * needs: "is this session inside a project that is still running".
155
+ *
156
+ * WHY THAT DISTINCTION MATTERS. A conversation that took part in a project which has since been
157
+ * completed is not a project member any more - it is a conversation that happens to have a
158
+ * finished project in its history. The old lookup mixed the two, so asking the Boss a question
159
+ * from such a conversation was refused with "a project member may not ask the Boss directly",
160
+ * which is not what was true: the project was over, and nobody was being bypassed. The finished
161
+ * project is still found and still visible in the history; it just does not block the question.
162
+ *
163
+ * A session that is in a running project AND in a finished one belongs to the running one - it
164
+ * is the one that is still in charge of it.
165
+ */
166
+ private liveProjectForSession;
167
+ canAskQuestion(conversationId: string): {
168
+ ok: boolean;
169
+ reason?: string;
170
+ };
95
171
  private reconcile;
96
172
  private resources;
97
173
  list(workspaceId?: string): Project[];
98
- headline(p: Project): string;
174
+ headline(p: Project, actor?: string): string;
99
175
  private isActorRelated;
100
176
  private projectSummary;
101
- listForActor(workspaceId: string, actor: string): ProjectSummary[];
102
- get(id: string, workspaceId?: string): Project;
177
+ /**
178
+ * THE PROJECTS AN ACTOR IS PART OF, AND HOW MANY OF THEM THERE ARE.
179
+ *
180
+ * The list is capped, because the list is what goes into an agent's context: a workspace that
181
+ * has been alive for a year has hundreds of finished projects, and handing all of them over
182
+ * answers the question "how are my projects doing" with a wall of history. The cap is on the
183
+ * RECENT ones - the same order the list has always had, newest update first - so what the
184
+ * agent reads first is what actually changed.
185
+ *
186
+ * AND THE COUNT IS TOLD, NOT IMPLIED. `total` is how many projects the session is really
187
+ * related to, counted BEFORE the cut, and `returned` is how many came back. An agent that sees
188
+ * total 57 and returned 20 knows the workspace has more history than it was shown, and says so
189
+ * instead of telling the Boss the workspace only has 20 projects. A silently truncated list
190
+ * would be a lie the agent could not see through.
191
+ */
192
+ listForActor(workspaceId: string, actor: string, limit?: number): ProjectListPage;
193
+ get(id: string, workspaceId?: string, actor?: string): Project;
103
194
  /**
104
195
  * THE PROJECT AS A REPORT A HUMAN CAN READ.
105
196
  *
@@ -175,7 +266,7 @@ export declare class ProjectService {
175
266
  raiseBudgetAndContinue(id: string, workspaceId: string, callerConversationId?: string): Promise<BudgetRaiseResult>;
176
267
  addReport(id: string, input: Omit<ProjectReport, "id" | "projectId" | "createdAt">, workspaceId?: string, actor?: string): Promise<ProjectReport>;
177
268
  addEscalation(id: string, input: Pick<ProjectEscalation, "reason" | "severity" | "taskId">, workspaceId?: string, actor?: string): Promise<ProjectEscalation>;
178
- resolveEscalation(id: string, escalationId: string, decision: string, status: "resolved" | "dismissed", workspaceId?: string): Promise<ProjectEscalation>;
269
+ resolveEscalation(id: string, escalationId: string, decision: string, status: "resolved" | "dismissed", workspaceId?: string, actor?: string): Promise<ProjectEscalation>;
179
270
  complete(id: string, summary: string, evidenceRefs: unknown, workspaceId: string, actor?: string): Promise<Project>;
180
271
  /**
181
272
  * The tasks an initiator hands over with create. A dependency names the TITLE of another task in