@chainingintention/pi-web-cn 1.202608.6 → 1.202608.8

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 (50) hide show
  1. package/dist/cli.js +8 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config.js +69 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/server/app.js +4 -1
  6. package/dist/server/app.js.map +1 -1
  7. package/dist/server/audit/managementAuditStore.js +211 -0
  8. package/dist/server/audit/managementAuditStore.js.map +1 -0
  9. package/dist/server/audit/normalToolAuditCli.js +143 -0
  10. package/dist/server/audit/normalToolAuditCli.js.map +1 -0
  11. package/dist/server/audit/normalToolAuditStore.js +264 -0
  12. package/dist/server/audit/normalToolAuditStore.js.map +1 -0
  13. package/dist/server/managementEmbed.js +19 -1
  14. package/dist/server/managementEmbed.js.map +1 -1
  15. package/dist/server/requestLogging.js +10 -7
  16. package/dist/server/requestLogging.js.map +1 -1
  17. package/dist/server/sessiond/sessionProxyRoutes.js +13 -7
  18. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  19. package/dist/server/sessiond.js +90 -4
  20. package/dist/server/sessiond.js.map +1 -1
  21. package/dist/server/sessions/managementPermissionSystem.js +8 -8
  22. package/dist/server/sessions/managementPermissionSystem.js.map +1 -1
  23. package/dist/server/sessions/piSessionService.js +133 -19
  24. package/dist/server/sessions/piSessionService.js.map +1 -1
  25. package/dist/server/sessions/sessionRoutes.js +6 -2
  26. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  27. package/dist/server/workbench/accessStateRoutes.js +30 -0
  28. package/dist/server/workbench/accessStateRoutes.js.map +1 -0
  29. package/dist/server/workbench/accessStateStore.js +29 -0
  30. package/dist/server/workbench/accessStateStore.js.map +1 -0
  31. package/dist/server/workbench/capabilitySearch.js +62 -0
  32. package/dist/server/workbench/capabilitySearch.js.map +1 -0
  33. package/dist/server/workbench/gatewayIntegration.js +56 -0
  34. package/dist/server/workbench/gatewayIntegration.js.map +1 -0
  35. package/dist/server/workbench/mcpClient.js +148 -0
  36. package/dist/server/workbench/mcpClient.js.map +1 -0
  37. package/dist/server/workbench/skillSync.js +375 -0
  38. package/dist/server/workbench/skillSync.js.map +1 -0
  39. package/dist/server/workbench/types.js +2 -0
  40. package/dist/server/workbench/types.js.map +1 -0
  41. package/dist/server/workbench/workbenchClient.js +136 -0
  42. package/dist/server/workbench/workbenchClient.js.map +1 -0
  43. package/dist/server/workbench/workbenchTools.js +145 -0
  44. package/dist/server/workbench/workbenchTools.js.map +1 -0
  45. package/dist/shared/apiTypes.d.ts +25 -0
  46. package/dist/shared/apiTypes.js.map +1 -1
  47. package/dist/shared/piWebConfigParsing.js +94 -0
  48. package/dist/shared/piWebConfigParsing.js.map +1 -1
  49. package/docs/config.md +65 -0
  50. package/package.json +7 -3
@@ -23,6 +23,7 @@ import { canonicalizeStoredCwd, cwdPathsEqual } from "../workingDirectory.js";
23
23
  import { readSessionHeaderSummary } from "./sessionFileHeader.js";
24
24
  import { countOutOfListingChildren, locateOutOfListingParents } from "./parentSessionLocator.js";
25
25
  import { siblingWorkspaceCwds } from "../workspaces/projectWorkspaceCwds.js";
26
+ import { workbenchAccessHandle } from "../managementEmbed.js";
26
27
  import { DEFAULT_EXTENSION_DIALOGS_TIMEOUT_MS, piWebDataDir } from "../../config.js";
27
28
  import { createScopedSettingsManager, resolveSettingsScopeDirectory } from "./projectSettingsScope.js";
28
29
  import { createAskUserToolDefinition } from "./askUserTool.js";
@@ -40,6 +41,8 @@ import { plainTextTheme } from "./plainTextTheme.js";
40
41
  import { SessionUnreadStore } from "./sessionUnreadStore.js";
41
42
  import { archiveCandidateFromActiveSession, archiveCandidateFromArchivedRecord, archiveCandidateFromListEntry, archiveInputFromActiveSession, archiveInputFromCandidate, archiveInputFromListEntry, clientSessionFromArchivedRecord, compareArchivedRecords, } from "./sessionArchiveMapping.js";
42
43
  import { annotateAssistantThinkingLevel, getBoolean, getProperty, getString, toClientEvent } from "./sessionUiEvents.js";
44
+ import { WorkbenchHttpError } from "../workbench/workbenchClient.js";
45
+ import { createWorkbenchToolDefinitions } from "../workbench/workbenchTools.js";
43
46
  const noopLogger = { info() { } };
44
47
  const DEFAULT_UNREAD_PUBLICATION_RETRY_MS = 1_000;
45
48
  /**
@@ -177,6 +180,22 @@ export function filterManagedProjectSkills(cwd, base) {
177
180
  }),
178
181
  };
179
182
  }
183
+ export function filterManagedWorkbenchSkills(cwd, base, receipt) {
184
+ const projectRoot = realpathSync(cwd);
185
+ const allowedDirectories = new Set(receipt.skills.map((skill) => resolve(projectRoot, ".pi", "skills", skill.directory)));
186
+ return {
187
+ ...base,
188
+ skills: base.skills.filter((skill) => {
189
+ try {
190
+ const path = realpathSync(skill.filePath);
191
+ return [...allowedDirectories].some((directory) => pathInsideOrEqual(directory, path));
192
+ }
193
+ catch {
194
+ return false;
195
+ }
196
+ }),
197
+ };
198
+ }
180
199
  function pathInsideOrEqual(parent, child) {
181
200
  const normalizedParent = resolve(parent);
182
201
  const normalizedChild = resolve(child);
@@ -318,11 +337,11 @@ function createRuntimeWithContextAndOneShotSessionOptions(createRuntime, managem
318
337
  });
319
338
  };
320
339
  }
321
- function createDefaultRuntimeFactory(normalModelRuntimeForCwd, managementModelRuntime, resolveSettingsScope, spawn, subsessions, askUser) {
340
+ function createDefaultRuntimeFactory(normalModelRuntimeForCwd, managementModelRuntime, resolveSettingsScope, spawn, subsessions, askUser, workbench, logger = noopLogger, managementAudit, managementProjectIdForCwd) {
322
341
  return async ({ cwd, agentDir, sessionManager, sessionStartEvent, managementContext, initialModel, initialThinkingLevel, delegationToolsEnabled = true }) => {
323
342
  const scopedModelRuntime = managementContext === undefined ? await normalModelRuntimeForCwd(cwd) : await managementModelRuntime();
324
343
  if (managementContext !== undefined) {
325
- return createManagementRuntimeFactory(scopedModelRuntime, resolveSettingsScope, spawn, subsessions, askUser, managementContext)({
344
+ return createManagementRuntimeFactory(scopedModelRuntime, resolveSettingsScope, spawn, subsessions, askUser, managementContext, workbench, logger, managementAudit, managementProjectIdForCwd)({
326
345
  cwd,
327
346
  agentDir,
328
347
  sessionManager,
@@ -350,9 +369,56 @@ function createDefaultRuntimeFactory(normalModelRuntimeForCwd, managementModelRu
350
369
  return { ...result, services, diagnostics: services.diagnostics };
351
370
  };
352
371
  }
353
- function createManagementRuntimeFactory(modelRuntime, resolveSettingsScope, spawn, subsessions, askUser, managementContext) {
372
+ function createManagementRuntimeFactory(modelRuntime, resolveSettingsScope, spawn, subsessions, askUser, managementContext, workbench, logger, managementAudit, managementProjectIdForCwd) {
354
373
  return async ({ cwd, agentDir, sessionManager, sessionStartEvent, initialModel, initialThinkingLevel, delegationToolsEnabled = true }) => {
355
- const policyAgentDir = await writeManagementPermissionSystemPolicy(agentDir, cwd, managementContext);
374
+ const auditIdentity = await resolveManagementAuditIdentity(cwd, managementContext, managementProjectIdForCwd, logger);
375
+ const accessHandle = workbenchAccessHandle(managementContext);
376
+ const state = workbench === undefined ? undefined : workbench.accessStates.require(accessHandle);
377
+ let receipt;
378
+ try {
379
+ receipt = state === undefined ? undefined : await workbench?.skills.synchronize(cwd, state, {
380
+ ...(managementAudit === undefined ? {} : { recorder: managementAudit }),
381
+ ...auditIdentity,
382
+ sessionId: sessionManager.getSessionId(),
383
+ cwd,
384
+ });
385
+ }
386
+ catch (error) {
387
+ if (error instanceof WorkbenchHttpError && error.status === 401 && accessHandle !== undefined)
388
+ workbench?.accessStates.delete(accessHandle);
389
+ throw error;
390
+ }
391
+ const workbenchTools = state === undefined || workbench === undefined ? [] : createWorkbenchToolDefinitions({
392
+ getState: () => workbench.accessStates.require(accessHandle),
393
+ workbench: workbench.client,
394
+ mcp: workbench.mcp,
395
+ invalidate: () => { if (accessHandle !== undefined)
396
+ workbench.accessStates.delete(accessHandle); },
397
+ logger,
398
+ ...(managementAudit === undefined ? {} : { audit: managementAudit }),
399
+ auditContext: {
400
+ ...auditIdentity,
401
+ sessionId: sessionManager.getSessionId(),
402
+ cwd,
403
+ },
404
+ });
405
+ const scopedSpawn = spawn === undefined
406
+ ? undefined
407
+ : (input) => spawn({ ...input, managementContext });
408
+ const scopedSubsessions = subsessions === undefined
409
+ ? undefined
410
+ : {
411
+ ...subsessions,
412
+ spawn: (input) => subsessions.spawn({ ...input, managementContext }),
413
+ };
414
+ const controlledManagementTools = [
415
+ ...(delegationToolsEnabled && scopedSpawn !== undefined ? [defineTool(createSpawnSessionToolDefinition(cwd, { spawn: scopedSpawn }))] : []),
416
+ ...(delegationToolsEnabled && scopedSubsessions !== undefined ? createSubsessionToolDefinitions(cwd, scopedSubsessions).map((tool) => defineTool(tool)) : []),
417
+ ...(askUser === undefined ? [] : [createAskUserToolDefinition(askUser)]),
418
+ ...workbenchTools,
419
+ ];
420
+ const controlledManagementToolNames = controlledManagementTools.map((tool) => tool.name);
421
+ const policyAgentDir = await writeManagementPermissionSystemPolicy(agentDir, cwd, managementContext, controlledManagementToolNames);
356
422
  return withRuntimeCreationEnvironment({ [PI_PERMISSION_SYSTEM_POLICY_AGENT_DIR]: policyAgentDir }, async () => {
357
423
  const settingsManager = await createScopedSettingsManager({
358
424
  cwd,
@@ -366,32 +432,21 @@ function createManagementRuntimeFactory(modelRuntime, resolveSettingsScope, spaw
366
432
  settingsManager,
367
433
  resourceLoaderOptions: {
368
434
  agentsFilesOverride: (base) => filterManagedGlobalContextFiles(cwd, agentDir, base),
369
- skillsOverride: (base) => filterManagedProjectSkills(cwd, base),
435
+ skillsOverride: (base) => receipt === undefined ? filterManagedProjectSkills(cwd, base) : filterManagedWorkbenchSkills(cwd, base, receipt),
370
436
  },
371
437
  });
372
438
  const managedToolOptions = createManagedAgentToolOptions(cwd);
373
- const scopedSpawn = spawn === undefined
374
- ? undefined
375
- : (input) => spawn({ ...input, managementContext });
376
- const scopedSubsessions = subsessions === undefined
377
- ? undefined
378
- : {
379
- ...subsessions,
380
- spawn: (input) => subsessions.spawn({ ...input, managementContext }),
381
- };
382
439
  const customTools = [
383
440
  ...managedAgentToolDefinitions(cwd, managedToolOptions),
384
441
  createManagedPythonToolDefinition(cwd, managementContext),
385
- ...(delegationToolsEnabled && scopedSpawn !== undefined ? [defineTool(createSpawnSessionToolDefinition(cwd, { spawn: scopedSpawn }))] : []),
386
- ...(delegationToolsEnabled && scopedSubsessions !== undefined ? createSubsessionToolDefinitions(cwd, scopedSubsessions).map((tool) => defineTool(tool)) : []),
387
- ...(askUser === undefined ? [] : [createAskUserToolDefinition(askUser)]),
442
+ ...controlledManagementTools,
388
443
  ];
389
444
  const options = {
390
445
  services,
391
446
  sessionManager,
392
447
  ...(sessionStartEvent === undefined ? {} : { sessionStartEvent }),
393
448
  customTools,
394
- tools: managementAgentToolNames(managementContext),
449
+ tools: managementAgentToolNames(managementContext, controlledManagementToolNames),
395
450
  ...(initialModel === undefined ? {} : { model: initialModel }),
396
451
  ...(initialThinkingLevel === undefined ? {} : { thinkingLevel: initialThinkingLevel }),
397
452
  };
@@ -521,6 +576,9 @@ export class PiSessionService {
521
576
  this.spawnTargets = deps.spawnTargets;
522
577
  this.projectWorkspaces = deps.projectWorkspaces;
523
578
  this.logger = deps.logger ?? noopLogger;
579
+ this.normalToolAudit = deps.normalToolAudit;
580
+ this.managementAudit = deps.managementAudit;
581
+ this.managementProjectIdForCwd = deps.managementProjectIdForCwd;
524
582
  this.now = deps.now ?? (() => new Date());
525
583
  this.notificationStore = deps.notificationStore ?? new SessionNotificationStore();
526
584
  this.unreadStore = deps.unreadStore ?? new SessionUnreadStore();
@@ -538,7 +596,7 @@ export class PiSessionService {
538
596
  list: (parentSessionId, parentSessionFile) => this.listSubsessions(parentSessionId, parentSessionFile),
539
597
  check: (parentSessionId, sessionId, parentSessionFile) => this.checkSubsession(parentSessionId, sessionId, parentSessionFile),
540
598
  read: (parentSessionId, sessionId, query, parentSessionFile) => this.readSubsession(parentSessionId, sessionId, query, parentSessionFile),
541
- }, deps.askUserEnabled === true ? { open: (input) => this.openAsk(input) } : undefined);
599
+ }, deps.askUserEnabled === true ? { open: (input) => this.openAsk(input) } : undefined, deps.workbench, this.logger, this.managementAudit, this.managementProjectIdForCwd);
542
600
  this.createAgentRuntime = deps.createAgentRuntime ?? defaultCreateAgentRuntime;
543
601
  this.workspaceActivity = deps.workspaceActivity;
544
602
  this.heartbeat = setInterval(() => { this.publishHeartbeats(); }, deps.heartbeatIntervalMs ?? 2000);
@@ -2435,6 +2493,7 @@ export class PiSessionService {
2435
2493
  }
2436
2494
  active.unsubscribe = session.subscribe((event) => {
2437
2495
  this.events.publish(session.sessionId, toClientEvent(event, session.thinkingLevel), active.eventScope);
2496
+ this.auditToolExecutionEvent(active, event);
2438
2497
  this.publishActivityForEvent(session, event);
2439
2498
  const eventType = getString(event, "type");
2440
2499
  if (eventType === "agent_end")
@@ -2763,6 +2822,46 @@ export class PiSessionService {
2763
2822
  if (this.hasActiveWork(session))
2764
2823
  this.publishActivity(session, "正在处理", "active");
2765
2824
  }
2825
+ auditToolExecutionEvent(active, event) {
2826
+ const eventType = getString(event, "type");
2827
+ if (eventType !== "tool_execution_start" && eventType !== "tool_execution_end")
2828
+ return;
2829
+ const isEnd = eventType === "tool_execution_end";
2830
+ const auditEvent = {
2831
+ sessionId: active.runtime.session.sessionId,
2832
+ cwd: active.runtime.cwd,
2833
+ toolName: getString(event, "toolName") ?? "unknown",
2834
+ toolCallId: getString(event, "toolCallId") ?? "unknown",
2835
+ status: isEnd ? (getBoolean(event, "isError") === true ? "failed" : "completed") : "started",
2836
+ };
2837
+ const managementIdentity = active.managementAuditIdentity;
2838
+ this.logger.info({
2839
+ mode: active.managementContextKey === undefined ? "normal" : "management",
2840
+ ...(managementIdentity ?? {}),
2841
+ ...auditEvent,
2842
+ }, "Pi tool execution audit");
2843
+ if (managementIdentity !== undefined) {
2844
+ try {
2845
+ this.managementAudit?.record({ action: "tool_execution", ...managementIdentity, ...auditEvent });
2846
+ }
2847
+ catch (error) {
2848
+ this.logger.info({ sessionId: auditEvent.sessionId, toolCallId: auditEvent.toolCallId, error: error instanceof Error ? error.message : String(error) }, "failed to enqueue management-mode tool audit");
2849
+ }
2850
+ return;
2851
+ }
2852
+ if (this.normalToolAudit === undefined)
2853
+ return;
2854
+ try {
2855
+ this.normalToolAudit.record(auditEvent);
2856
+ }
2857
+ catch (error) {
2858
+ this.logger.info({
2859
+ sessionId: auditEvent.sessionId,
2860
+ toolCallId: auditEvent.toolCallId,
2861
+ error: error instanceof Error ? error.message : String(error),
2862
+ }, "failed to persist ordinary-mode tool audit");
2863
+ }
2864
+ }
2766
2865
  /**
2767
2866
  * Build the reporter for one session construction.
2768
2867
  *
@@ -3036,6 +3135,9 @@ export class PiSessionService {
3036
3135
  unsubscribe: noop,
3037
3136
  managementContextKey: managementContextKey(options.managementContext),
3038
3137
  eventScope: eventScopeFromManagementContext(options.managementContext),
3138
+ ...(options.managementContext === undefined ? {} : {
3139
+ managementAuditIdentity: await resolveManagementAuditIdentity(cwd, options.managementContext, this.managementProjectIdForCwd, this.logger),
3140
+ }),
3039
3141
  };
3040
3142
  let boundSession = runtime.session;
3041
3143
  let notificationGeneration = options.notificationGeneration;
@@ -3708,4 +3810,16 @@ function finalAssistantText(messages) {
3708
3810
  function isRecord(value) {
3709
3811
  return typeof value === "object" && value !== null;
3710
3812
  }
3813
+ async function resolveManagementAuditIdentity(cwd, context, resolveProjectId, logger) {
3814
+ let projectId = context.projects[0]?.id ?? "default-project";
3815
+ if (resolveProjectId !== undefined) {
3816
+ try {
3817
+ projectId = await resolveProjectId(cwd, context);
3818
+ }
3819
+ catch (error) {
3820
+ logger.info({ userId: context.user.id, cwd, error: error instanceof Error ? error.message : String(error) }, "failed to resolve management audit project; using the authorized default");
3821
+ }
3822
+ }
3823
+ return { userId: context.user.id, rootUserId: context.user.rootUserId, projectId };
3824
+ }
3711
3825
  //# sourceMappingURL=piSessionService.js.map