@evandu/super-pi 0.1.155 → 0.1.157

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 (33) hide show
  1. package/agents/super-pi-task/core.ts +1 -1
  2. package/agents/super-pi-task/index.ts +25 -3
  3. package/dist/client/.super-pi-client-sources.json +3 -3
  4. package/dist/client/assets/{CodeMirrorEditor-DGf8TiZ5.js → CodeMirrorEditor-BB0GyCns.js} +2 -2
  5. package/dist/client/assets/{dist-C2L242qg.js → dist-BBvvLw1T.js} +1 -1
  6. package/dist/client/assets/{dist-xrtSXFwN.js → dist-BCZnYgeb.js} +1 -1
  7. package/dist/client/assets/{dist-BfHhQNXd.js → dist-BDxBEqt3.js} +1 -1
  8. package/dist/client/assets/{dist-BVkFNoQK.js → dist-BJPYIQat.js} +1 -1
  9. package/dist/client/assets/{dist-CHNbJU4G.js → dist-BQK5OciP.js} +1 -1
  10. package/dist/client/assets/{dist-DwmCtvaQ.js → dist-BQjIO_bJ.js} +1 -1
  11. package/dist/client/assets/{dist-DNTb20de.js → dist-BT4A5Gts.js} +1 -1
  12. package/dist/client/assets/{dist-tKodxnRd.js → dist-BZNEPjC8.js} +1 -1
  13. package/dist/client/assets/{dist-a0CnDI4Q.js → dist-BZlYaGxq.js} +1 -1
  14. package/dist/client/assets/{dist-CrmwJDXn.js → dist-BeCgVWwJ.js} +1 -1
  15. package/dist/client/assets/{dist-BlgLF_Op.js → dist-CH0-OHWZ.js} +1 -1
  16. package/dist/client/assets/{dist-WK6R59tK.js → dist-D4twfstd.js} +1 -1
  17. package/dist/client/assets/{dist-BFK3Zdz3.js → dist-D8Zpt_js.js} +1 -1
  18. package/dist/client/assets/{dist-gE9RBFPO.js → dist-DIgk0yLa.js} +1 -1
  19. package/dist/client/assets/{dist-DalOgxpu.js → dist-DSE_w05B.js} +1 -1
  20. package/dist/client/assets/{dist-BQiNco13.js → dist-DdCTaTc1.js} +1 -1
  21. package/dist/client/assets/{dist-DLvthSbR.js → dist-DndOTsBF.js} +1 -1
  22. package/dist/client/assets/{dist-BErJtr8L.js → dist-DuhZOzpj.js} +1 -1
  23. package/dist/client/assets/{dist-CLKVZWaL.js → dist-DxoyfcUP.js} +1 -1
  24. package/dist/client/assets/{dist-DigKGTQO.js → dist-R_rLoe1G.js} +1 -1
  25. package/dist/client/assets/{dist-DQlz7q1d.js → dist-W281b4I1.js} +1 -1
  26. package/dist/client/assets/{dist-D0KF79YG.js → dist-rXWbM1pb.js} +1 -1
  27. package/dist/client/assets/{index-Bntn9Qzg.js → index-C2caXFC3.js} +3 -3
  28. package/dist/client/assets/{index-HB8NmRIH.css → index-OdImWK16.css} +1 -1
  29. package/dist/client/index.html +2 -2
  30. package/dist/server/application/project-service.d.ts +20 -1
  31. package/dist/server/application/project-service.js +120 -7
  32. package/dist/server/types.d.ts +3 -0
  33. package/package.json +1 -1
@@ -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-Bntn9Qzg.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-HB8NmRIH.css">
11
+ <script type="module" crossorigin src="/assets/index-C2caXFC3.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-OdImWK16.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -19,6 +19,7 @@ export type CreateProjectInput = {
19
19
  defaultLeaderProfile?: unknown;
20
20
  defaultExecutorProfile?: unknown;
21
21
  defaultReviewerProfile?: unknown;
22
+ initiatorConversationId?: string;
22
23
  };
23
24
  export type AddTaskInput = Partial<ProjectTask> & {
24
25
  title: string;
@@ -26,6 +27,15 @@ export type AddTaskInput = Partial<ProjectTask> & {
26
27
  executorProfile?: unknown;
27
28
  reviewerProfile?: unknown;
28
29
  };
30
+ /** A top-level task an initiator hands over with create, before the service gives it an ID. */
31
+ export type TopLevelTaskPlan = {
32
+ title: string;
33
+ objective: string;
34
+ deliverables?: string[];
35
+ acceptanceCriteria?: string[];
36
+ priority?: ProjectTask["priority"];
37
+ dependencies: string[];
38
+ };
29
39
  /** Persist explicit domain fields only; transport credentials never enter project records. */
30
40
  export declare class ProjectService {
31
41
  private readonly store;
@@ -40,6 +50,7 @@ export declare class ProjectService {
40
50
  private change;
41
51
  private live;
42
52
  private leader;
53
+ private topLevelAuthor;
43
54
  private manager;
44
55
  private requiredComplete;
45
56
  private task;
@@ -49,7 +60,7 @@ export declare class ProjectService {
49
60
  list(workspaceId?: string): Project[];
50
61
  get(id: string, workspaceId?: string): Project;
51
62
  projectForSession(conversationId: string): Project | undefined;
52
- create(input: CreateProjectInput): Promise<Project>;
63
+ create(input: CreateProjectInput, initiatorConversationId?: string): Promise<Project>;
53
64
  private admission;
54
65
  private context;
55
66
  start(id: string, workspaceId: string, actor?: string): Promise<Project>;
@@ -77,5 +88,13 @@ export declare class ProjectService {
77
88
  addEscalation(id: string, input: Pick<ProjectEscalation, "reason" | "severity" | "taskId">, workspaceId?: string, actor?: string): Promise<ProjectEscalation>;
78
89
  resolveEscalation(id: string, escalationId: string, decision: string, status: "resolved" | "dismissed", workspaceId?: string): Promise<ProjectEscalation>;
79
90
  complete(id: string, summary: string, evidenceRefs: unknown, workspaceId: string, actor?: string): Promise<Project>;
91
+ /**
92
+ * The tasks an initiator hands over with create. A dependency names the TITLE of another task in
93
+ * the same batch, because the batch carries no task IDs yet - the service assigns them while it
94
+ * registers the batch. The plan comes back in dependency order, so the plain add_task rules
95
+ * (title, objective, priority, sibling dependencies) can be reused unchanged.
96
+ */
97
+ private planTopLevelTasks;
98
+ private registerTopLevelTasks;
80
99
  tool(body: Record<string, any>, workspaceId: string, actor: string): Promise<unknown>;
81
100
  }
@@ -177,6 +177,15 @@ export class ProjectService {
177
177
  throw new Error("项目已结束,不能继续修改或派发"); }
178
178
  leader(p, actor) { if (actor && actor !== p.ownerConversationId)
179
179
  throw new Error("仅项目负责人可以执行此操作"); }
180
+ topLevelAuthor(p, actor) {
181
+ if (!actor)
182
+ return;
183
+ if (actor === p.ownerConversationId)
184
+ return;
185
+ if (actor === p.initiatorConversationId)
186
+ return;
187
+ throw new Error("只有项目负责人或发起人可以登记顶层任务");
188
+ }
180
189
  manager(p, t, actor) {
181
190
  const manager = t.parentTaskId ? this.task(p, t.parentTaskId).ownerConversationId : p.ownerConversationId;
182
191
  if (actor && actor !== manager)
@@ -194,7 +203,7 @@ export class ProjectService {
194
203
  return t;
195
204
  }
196
205
  member(p, actor) {
197
- if (actor && actor !== p.ownerConversationId && !p.tasks.some((t) => t.ownerConversationId === actor || t.reviewerConversationId === actor))
206
+ if (actor && actor !== p.ownerConversationId && actor !== p.initiatorConversationId && !p.tasks.some((t) => t.ownerConversationId === actor || t.reviewerConversationId === actor))
198
207
  throw new Error("调用会话不属于项目执行团队");
199
208
  }
200
209
  reconcile(p) {
@@ -250,7 +259,7 @@ export class ProjectService {
250
259
  }
251
260
  return undefined;
252
261
  }
253
- async create(input) {
262
+ async create(input, initiatorConversationId) {
254
263
  const workspaceId = this.workspace(input.workspaceId);
255
264
  if (input.ownerConversationId !== undefined)
256
265
  throw new Error("项目负责人由启动操作创建独立会话");
@@ -258,12 +267,16 @@ export class ProjectService {
258
267
  throw new Error("项目风险等级不合法");
259
268
  if (input.budget !== undefined && (!input.budget || typeof input.budget !== "object" || Array.isArray(input.budget)))
260
269
  throw new Error("资源预算不合法");
261
- for (const [key, value] of Object.entries(input.budget ?? {}))
270
+ for (const [key, value] of Object.entries(input.budget ?? {})) {
271
+ if (value === undefined)
272
+ continue;
262
273
  if (!["maxTokens", "maxConcurrent"].includes(key) || !Number.isSafeInteger(value) || value <= 0)
263
274
  throw new Error("资源预算必须是正整数");
275
+ }
264
276
  const defaultLeaderProfile = profile(input.defaultLeaderProfile, "默认 Leader 配置");
265
277
  const defaultExecutorProfile = profile(input.defaultExecutorProfile, "默认执行配置");
266
278
  const defaultReviewerProfile = profile(input.defaultReviewerProfile, "默认复核配置");
279
+ const initiator = (initiatorConversationId ?? (typeof input.initiatorConversationId === "string" ? input.initiatorConversationId : undefined))?.trim() || undefined;
267
280
  const now = stamp();
268
281
  const p = { id: `project_${randomUUID()}`, workspaceId, title: text(input.title, "项目标题"), objective: text(input.objective, "项目目标"),
269
282
  deliverables: strings(input.deliverables, "交付物"), acceptanceCriteria: strings(input.acceptanceCriteria, "验收标准"), constraints: strings(input.constraints, "约束"),
@@ -271,8 +284,14 @@ export class ProjectService {
271
284
  ...(defaultLeaderProfile ? { defaultLeaderProfile } : {}),
272
285
  ...(defaultExecutorProfile ? { defaultExecutorProfile } : {}),
273
286
  ...(defaultReviewerProfile ? { defaultReviewerProfile } : {}),
287
+ ...(initiator ? { initiatorConversationId: initiator } : {}),
274
288
  tasks: [], reports: [], escalations: [], events: [], createdAt: now, updatedAt: now };
275
- this.event(p, "created", "Boss 触发项目");
289
+ if (initiator) {
290
+ this.event(p, "created", "Agent 触发项目", undefined, initiator);
291
+ }
292
+ else {
293
+ this.event(p, "created", "Boss 触发项目");
294
+ }
276
295
  const saved = await this.store.setProject(p);
277
296
  return this.projectView(saved);
278
297
  }
@@ -303,7 +322,7 @@ export class ProjectService {
303
322
  }
304
323
  async start(id, workspaceId, actor) {
305
324
  return this.change(id, workspaceId, async (p) => {
306
- this.leader(p, actor);
325
+ this.topLevelAuthor(p, actor);
307
326
  this.admission(p, false);
308
327
  if (!this.orchestrator)
309
328
  throw new Error("会话编排服务未就绪");
@@ -334,7 +353,9 @@ export class ProjectService {
334
353
  return this.change(id, workspaceId, (p) => {
335
354
  this.live(p);
336
355
  const parentTaskId = optional(input.parentTaskId, "父任务 id");
337
- let manager = p.ownerConversationId;
356
+ // A top-level task is accountable to whoever owns the project: the leader once it runs, and
357
+ // before that the initiator who registered it - a task nobody answers for cannot be closed.
358
+ let manager = p.ownerConversationId ?? p.initiatorConversationId;
338
359
  if (parentTaskId) {
339
360
  const parent = this.task(p, parentTaskId);
340
361
  if (parent.role !== "leader" || parent.status !== "in_progress" || !parent.ownerConversationId)
@@ -352,7 +373,7 @@ export class ProjectService {
352
373
  throw new Error("Leader 最多 3 层,请改为执行任务");
353
374
  }
354
375
  else
355
- this.leader(p, actor);
376
+ this.topLevelAuthor(p, actor);
356
377
  if (input.role !== undefined && input.role !== "leader" && input.role !== "executor")
357
378
  throw new Error("任务角色必须是 leader 或 executor");
358
379
  const dependencies = strings(input.dependencies, "任务依赖");
@@ -723,12 +744,104 @@ export class ProjectService {
723
744
  return p;
724
745
  });
725
746
  }
747
+ /**
748
+ * The tasks an initiator hands over with create. A dependency names the TITLE of another task in
749
+ * the same batch, because the batch carries no task IDs yet - the service assigns them while it
750
+ * registers the batch. The plan comes back in dependency order, so the plain add_task rules
751
+ * (title, objective, priority, sibling dependencies) can be reused unchanged.
752
+ */
753
+ planTopLevelTasks(batch) {
754
+ if (batch === undefined || batch === null)
755
+ return [];
756
+ if (!Array.isArray(batch))
757
+ throw new Error("tasks 必须是任务数组");
758
+ if (batch.length > 100)
759
+ throw new Error("项目最多 100 个任务,请收敛分工范围");
760
+ const items = batch.map((raw) => {
761
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
762
+ throw new Error("tasks 的每一项必须是任务对象");
763
+ const record = raw;
764
+ const priority = record.priority;
765
+ if (priority !== undefined && !["low", "medium", "high", "urgent"].includes(String(priority)))
766
+ throw new Error("任务优先级不合法");
767
+ return {
768
+ title: text(record.title, "任务标题"),
769
+ objective: text(record.objective, "任务目标"),
770
+ deliverables: record.deliverables === undefined ? undefined : strings(record.deliverables, "任务交付物"),
771
+ acceptanceCriteria: record.acceptanceCriteria === undefined ? undefined : strings(record.acceptanceCriteria, "任务验收标准"),
772
+ priority: priority,
773
+ dependencies: record.dependencies === undefined ? [] : strings(record.dependencies, "任务依赖"),
774
+ };
775
+ });
776
+ const index = new Map();
777
+ items.forEach((item, at) => {
778
+ if (index.has(item.title))
779
+ throw new Error(`任务标题重复:${item.title}`);
780
+ index.set(item.title, at);
781
+ });
782
+ const edges = items.map((item) => item.dependencies.map((dependency) => {
783
+ const at = index.get(dependency);
784
+ if (at === undefined)
785
+ throw new Error(`任务依赖不存在:${dependency}`);
786
+ return at;
787
+ }));
788
+ const ordered = [];
789
+ const done = new Set();
790
+ while (ordered.length < items.length) {
791
+ let progressed = false;
792
+ for (let at = 0; at < items.length; at += 1) {
793
+ if (done.has(at))
794
+ continue;
795
+ if (!edges[at].every((dependency) => done.has(dependency)))
796
+ continue;
797
+ ordered.push(items[at]);
798
+ done.add(at);
799
+ progressed = true;
800
+ }
801
+ if (!progressed)
802
+ throw new Error("任务依赖存在循环,无法登记顶层任务");
803
+ }
804
+ return ordered;
805
+ }
806
+ async registerTopLevelTasks(projectId, plan, workspaceId, actor) {
807
+ const ids = new Map();
808
+ for (const item of plan) {
809
+ const dependencies = item.dependencies.map((dependency) => ids.get(dependency));
810
+ const task = await this.addTask(projectId, {
811
+ title: item.title, objective: item.objective,
812
+ ...(item.deliverables ? { deliverables: item.deliverables } : {}),
813
+ ...(item.acceptanceCriteria ? { acceptanceCriteria: item.acceptanceCriteria } : {}),
814
+ ...(item.priority !== undefined ? { priority: item.priority } : {}),
815
+ dependencies,
816
+ }, workspaceId, actor);
817
+ ids.set(item.title, task.id);
818
+ }
819
+ }
726
820
  async tool(body, workspaceId, actor) {
821
+ if (body.action === "create") {
822
+ // ONLY A ROOT CONVERSATION MAY OPEN A PROJECT. Every dispatched session - the leader, its
823
+ // reviewers, the workers - holds the same tool, and a project opened from one of them would
824
+ // carry its own budget and concurrency guards while sitting outside the guard that dispatched it.
825
+ const conversation = this.store.conversation(actor);
826
+ if (!conversation)
827
+ throw new Error("找不到这个会话。");
828
+ if (conversation.parentConversationId)
829
+ throw new Error("只有主对话可以创建项目:派发出来的会话不能另起项目,否则会绕过项目的预算与并发护栏。");
830
+ const running = this.store.projects().filter((p) => p.initiatorConversationId === actor && !closed(p));
831
+ if (running.length >= 3)
832
+ throw new Error("你同时进行的项目超过上限(3 个),先完成或停止一个再新建。");
833
+ const { tasks: batch, ...input } = body;
834
+ const plan = this.planTopLevelTasks(batch);
835
+ const project = await this.create({ ...input, workspaceId }, actor);
836
+ await this.registerTopLevelTasks(project.id, plan, workspaceId, actor);
837
+ return this.get(project.id, workspaceId);
838
+ }
727
839
  const id = text(body.projectId, "projectId");
728
840
  const p = this.get(id, workspaceId);
729
841
  this.member(p, actor);
730
842
  switch (body.action) {
731
843
  case "get": return p;
844
+ case "start": return this.start(id, workspaceId, actor);
732
845
  case "add_task": return this.addTask(id, body, workspaceId, actor);
733
846
  case "dispatch": return this.dispatchTask(id, text(body.taskId, "taskId"), workspaceId, actor);
734
847
  case "submit": return this.submit(id, text(body.taskId, "taskId"), body, workspaceId, actor);
@@ -996,6 +996,9 @@ export type Project = {
996
996
  risk: ProjectRisk;
997
997
  status: ProjectStatus;
998
998
  ownerConversationId?: string;
999
+ /** The session that created the project - the Boss's own conversation, which may register the
1000
+ * top-level tasks and is the one the project answers to. */
1001
+ initiatorConversationId?: string;
999
1002
  budget?: {
1000
1003
  maxTokens?: number;
1001
1004
  maxConcurrent?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evandu/super-pi",
3
- "version": "0.1.155",
3
+ "version": "0.1.157",
4
4
  "description": "Web control plane and companion for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",