fuli-context 0.3.0 → 0.4.0

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 (87) hide show
  1. package/README.md +0 -24
  2. package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +1 -0
  3. package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +1 -0
  4. package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +1 -0
  5. package/dist/web/assets/KnowledgeOrganizerPage-CJCNI2Wd.css +1 -0
  6. package/dist/web/assets/KnowledgeOrganizerPage-PXH-TlGt.js +1 -0
  7. package/dist/web/assets/KnowledgePage-CNQXd15y.js +1 -0
  8. package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +4 -0
  9. package/dist/web/assets/OverviewPage-ul7g7HXh.js +1 -0
  10. package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +1 -0
  11. package/dist/web/assets/PersonalProfilePage-S8ZcG2LU.css +1 -0
  12. package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +1 -0
  13. package/dist/web/assets/PublicProjectsPage-C700sR7z.js +1 -0
  14. package/dist/web/assets/ReviewPage-7MDDUDgA.js +1 -0
  15. package/dist/web/assets/console-B_Ena2DS.js +5 -0
  16. package/dist/web/assets/identity-CjBwYSMX.js +1 -0
  17. package/dist/web/assets/index-BQRkOwPx.js +2 -0
  18. package/dist/web/assets/{index-Devf7glZ.css → index-CpLDx3of.css} +1 -1
  19. package/dist/web/assets/model-C-5RVIJ2.js +1 -0
  20. package/dist/web/assets/{paths-B790iniO.js → paths-BkULqmGI.js} +1 -1
  21. package/dist/web/assets/{vue-router-BYTAoWT3.js → vue-router-DnD3hUm7.js} +1 -1
  22. package/dist/web/index.html +5 -6
  23. package/graph-provider/fuli_graph/app.py +48 -0
  24. package/graph-provider/fuli_graph/common_knowledge.py +443 -0
  25. package/graph-provider/fuli_graph/common_knowledge_models.py +54 -0
  26. package/graph-provider/fuli_graph/graph_models.py +9 -0
  27. package/graph-provider/fuli_graph/graph_query.py +25 -0
  28. package/graph-provider/fuli_graph/knowledge_audit.py +211 -0
  29. package/graph-provider/fuli_graph/knowledge_feedback_models.py +69 -0
  30. package/graph-provider/fuli_graph/knowledge_management.py +37 -0
  31. package/graph-provider/fuli_graph/knowledge_search.py +24 -2
  32. package/graph-provider/fuli_graph/models.py +69 -0
  33. package/graph-provider/fuli_graph/search_projection.py +59 -0
  34. package/graph-provider/fuli_graph/store_knowledge.py +2 -0
  35. package/graph-provider/pyproject.toml +1 -1
  36. package/npm-shrinkwrap.json +97 -2
  37. package/package.json +2 -1
  38. package/skills/capturing-session-knowledge/SKILL.md +83 -13
  39. package/src/agent-tools/graph-definitions.js +181 -13
  40. package/src/agent-tools/graph-handlers.js +19 -0
  41. package/src/cli/command-registry.js +1 -56
  42. package/src/cli.js +2 -37
  43. package/src/graphiti/agent-knowledge-workflows.js +543 -0
  44. package/src/graphiti/federated-application.js +85 -104
  45. package/src/graphiti/knowledge-provider-mapping.js +91 -0
  46. package/src/graphiti/provider-client.js +15 -0
  47. package/src/graphiti/source-uri.js +29 -0
  48. package/src/mcp/common-knowledge-preview-tokens.js +67 -0
  49. package/src/mcp/create-mcp-server.js +43 -7
  50. package/src/mcp/instructions.js +6 -7
  51. package/src/mcp/lifecycle-audit.js +27 -0
  52. package/src/mcp/task-context-registry.js +93 -0
  53. package/src/mcp/tool-annotations.js +16 -1
  54. package/src/mcp/tool-result.js +24 -0
  55. package/src/runtime-options.js +7 -8
  56. package/src/setup/agent-installation-status.js +14 -2
  57. package/src/setup/agents.js +12 -1
  58. package/src/setup/claude-code-config.js +194 -0
  59. package/src/setup/config-backup.js +20 -5
  60. package/dist/web/assets/ConnectionsPage-CY6H_mJJ.js +0 -1
  61. package/dist/web/assets/KnowledgeInspector-BSpz5Sfm.js +0 -1
  62. package/dist/web/assets/KnowledgeInspector-CHbB_0rC.css +0 -1
  63. package/dist/web/assets/KnowledgeOrganizerPage-BlEvhPrv.css +0 -1
  64. package/dist/web/assets/KnowledgeOrganizerPage-CvjGPlUc.js +0 -1
  65. package/dist/web/assets/KnowledgePage-Khuo3cNs.js +0 -1
  66. package/dist/web/assets/KnowledgeWorkspace-DVZusvem.js +0 -4
  67. package/dist/web/assets/OverviewPage-DC4C4Fhn.js +0 -1
  68. package/dist/web/assets/PersonalProfilePage-CSV9Mtgz.js +0 -1
  69. package/dist/web/assets/PersonalProfilePage-CVJ3TcRi.css +0 -1
  70. package/dist/web/assets/PersonalProjectsPage-A50EaseQ.js +0 -1
  71. package/dist/web/assets/PublicProjectsPage-BAdO_bae.js +0 -1
  72. package/dist/web/assets/ReviewPage-Dztk2jDG.js +0 -1
  73. package/dist/web/assets/console-tbvTUi6p.js +0 -1
  74. package/dist/web/assets/identity-B3s5CXhv.js +0 -1
  75. package/dist/web/assets/index-DwUDhQKc.js +0 -6
  76. package/dist/web/assets/model-DVT8sjy9.js +0 -1
  77. package/dist/web/assets/runtime-dom.esm-bundler-DttsK21F.js +0 -1
  78. package/src/cli/commands/candidates.js +0 -17
  79. package/src/cli/commands/ingestion.js +0 -32
  80. package/src/cli/commands/reads.js +0 -50
  81. package/src/cli/commands/spaces.js +0 -20
  82. package/src/cli/invocation.js +0 -47
  83. package/src/cli/migrate-command.js +0 -71
  84. package/src/cli/migration-destination.js +0 -117
  85. package/src/storage/import-json.js +0 -70
  86. package/src/storage/legacy-record-validation.js +0 -152
  87. package/src/storage/legacy-snapshot-validation.js +0 -32
@@ -0,0 +1,93 @@
1
+ import { randomUUID } from 'node:crypto';
2
+
3
+ const MAX_ACTIVE_SESSIONS = 256;
4
+
5
+ export class TaskContextRegistry {
6
+ constructor({ tokenFactory = () => `fuli-task-${randomUUID()}` } = {}) {
7
+ this.tokenFactory = tokenFactory;
8
+ this.sessions = new Map();
9
+ this.tokens = new Map();
10
+ }
11
+
12
+ begin({ sessionId, personalProjectId }) {
13
+ assertNonEmpty(sessionId, 'sessionId');
14
+ const previous = this.sessions.get(sessionId) ?? null;
15
+ if (previous) this.tokens.delete(previous.token);
16
+
17
+ const task = {
18
+ token: this.tokenFactory(),
19
+ sessionId,
20
+ personalProjectId: personalProjectId ?? null,
21
+ checkpoint: null
22
+ };
23
+ this.sessions.delete(sessionId);
24
+ this.sessions.set(sessionId, task);
25
+ this.tokens.set(task.token, task);
26
+ this.#evictOldest();
27
+ return {
28
+ ...task,
29
+ previousCheckpointMissing: Boolean(previous && !previous.checkpoint)
30
+ };
31
+ }
32
+
33
+ checkpoint(token, checkpoint) {
34
+ assertNonEmpty(token, 'taskContextToken');
35
+ const task = this.tokens.get(token);
36
+ if (!task) throw new TypeError('Unknown or expired Fuli task context token');
37
+ if (task.checkpoint) {
38
+ if (sameCheckpoint(task.checkpoint, checkpoint)) return task;
39
+ throw new TypeError('Fuli task context has already been checkpointed');
40
+ }
41
+ task.checkpoint = Object.freeze({ ...checkpoint });
42
+ return task;
43
+ }
44
+
45
+ context(token) {
46
+ assertNonEmpty(token, 'taskContextToken');
47
+ const task = this.tokens.get(token);
48
+ if (!task) throw new TypeError('Unknown or expired Fuli task context token');
49
+ return task;
50
+ }
51
+
52
+ verify(sessionId) {
53
+ assertNonEmpty(sessionId, 'sessionId');
54
+ const task = this.sessions.get(sessionId);
55
+ if (!task) {
56
+ return {
57
+ status: 'not_started',
58
+ guidance: 'No Fuli task context was started; do not block the Agent.'
59
+ };
60
+ }
61
+ if (task.checkpoint) {
62
+ return {
63
+ status: 'checkpointed',
64
+ disposition: task.checkpoint.disposition
65
+ };
66
+ }
67
+ return {
68
+ status: 'checkpoint_required',
69
+ decision: 'block',
70
+ reason: 'Before finishing, call checkpoint_task_knowledge once with either a bounded durable candidate batch or retain_nothing. Do not store raw transcripts, guesses, or temporary output.'
71
+ };
72
+ }
73
+
74
+ #evictOldest() {
75
+ while (this.sessions.size > MAX_ACTIVE_SESSIONS) {
76
+ const [sessionId, task] = this.sessions.entries().next().value;
77
+ this.sessions.delete(sessionId);
78
+ this.tokens.delete(task.token);
79
+ }
80
+ }
81
+ }
82
+
83
+ function assertNonEmpty(value, label) {
84
+ if (typeof value !== 'string' || !value.trim()) {
85
+ throw new TypeError(`${label} must be a nonempty string`);
86
+ }
87
+ }
88
+
89
+ function sameCheckpoint(left, right) {
90
+ return left.disposition === right.disposition
91
+ && left.reason === right.reason
92
+ && left.captureStatus === right.captureStatus;
93
+ }
@@ -1,4 +1,8 @@
1
1
  const READ_TOOLS = new Set([
2
+ 'verify_task_checkpoint',
3
+ 'search_current_project_knowledge',
4
+ 'discover_common_knowledge_candidates',
5
+ 'preview_common_knowledge_promotion',
2
6
  'list_knowledge_spaces',
3
7
  'list_personal_projects',
4
8
  'preview_personal_project_action',
@@ -10,11 +14,16 @@ const READ_TOOLS = new Set([
10
14
  ]);
11
15
 
12
16
  const WRITE_TOOLS = new Set([
17
+ 'begin_task_context',
18
+ 'checkpoint_task_knowledge',
13
19
  'get_collaboration_preferences',
14
20
  'resolve_deferred_preference_conflict',
15
21
  'capture_session_knowledge',
22
+ 'record_decision_trace',
16
23
  'search_knowledge_graph',
17
24
  'record_knowledge_usage',
25
+ 'record_knowledge_feedback',
26
+ 'apply_common_knowledge_promotion',
18
27
  'get_knowledge_graph',
19
28
  'search_human_knowledge_changes',
20
29
  'review_human_knowledge_change',
@@ -34,6 +43,7 @@ const WRITE_TOOLS = new Set([
34
43
 
35
44
  const DESTRUCTIVE_TOOLS = new Set([
36
45
  'resolve_deferred_preference_conflict',
46
+ 'apply_common_knowledge_promotion',
37
47
  'review_project_proposal',
38
48
  'review_project_relation',
39
49
  'review_personal_draft',
@@ -50,8 +60,13 @@ export function annotationsFor(name) {
50
60
  destructiveHint: DESTRUCTIVE_TOOLS.has(name),
51
61
  idempotentHint: readOnly || name === 'upsert_personal_project' ||
52
62
  name === 'publish_personal_project' || name === 'unsubscribe_public_project' ||
53
- name === 'record_knowledge_usage',
63
+ name === 'record_knowledge_usage' || name === 'record_decision_trace' ||
64
+ name === 'record_knowledge_feedback' ||
65
+ name === 'checkpoint_task_knowledge',
54
66
  openWorldHint: name === 'capture_session_knowledge' ||
67
+ name === 'record_decision_trace' ||
68
+ name === 'record_knowledge_feedback' ||
69
+ name === 'checkpoint_task_knowledge' ||
55
70
  name === 'publish_personal_project' ||
56
71
  name === 'create_project_relation' ||
57
72
  name === 'review_project_relation' ||
@@ -32,6 +32,30 @@ export function successToolResult(value, { limitBytes = RESULT_LIMIT_BYTES } = {
32
32
  };
33
33
  }
34
34
 
35
+ export function hookAdditionalContextToolResult(
36
+ value,
37
+ {
38
+ hookEventName,
39
+ label,
40
+ limitBytes = RESULT_LIMIT_BYTES
41
+ }
42
+ ) {
43
+ const result = successToolResult(value, { limitBytes });
44
+ const additionalContext = `${label}\n${JSON.stringify(result.structuredContent)}`;
45
+ return {
46
+ ...result,
47
+ content: [{
48
+ type: 'text',
49
+ text: JSON.stringify({
50
+ hookSpecificOutput: {
51
+ hookEventName,
52
+ additionalContext
53
+ }
54
+ })
55
+ }]
56
+ };
57
+ }
58
+
35
59
  export function errorToolResult(error) {
36
60
  const controlled = error instanceof ApplicationError;
37
61
  const code = controlled ? error.code : 'internal_error';
@@ -25,7 +25,7 @@ export function resolveRuntimeOptions(args = [], env = process.env, {
25
25
  let defaultDbPath = systemDbPath;
26
26
  if (cliDbPath === null && envDbPath === null) {
27
27
  defaultDbPath = resolveDefaultDbPath({ cwd, existsSync, systemDbPath });
28
- guardImplicitDefaultUpgrade({ cwd, existsSync, systemDbPath });
28
+ guardImplicitDefaultUpgrade({ cwd, existsSync });
29
29
  }
30
30
  return {
31
31
  dbPath: cliDbPath ?? envDbPath ?? defaultDbPath,
@@ -36,8 +36,7 @@ export function resolveRuntimeOptions(args = [], env = process.env, {
36
36
 
37
37
  export function resolveStore({ dbPath }) {
38
38
  if (extname(dbPath).toLowerCase() === '.json') {
39
- const destination = dbPath.slice(0, -extname(dbPath).length) + '.db';
40
- throw new RuntimeConfigurationError(migrationMessage(dbPath, destination));
39
+ throw new RuntimeConfigurationError(legacyJsonMessage(dbPath));
41
40
  }
42
41
  return openSqliteStore(dbPath);
43
42
  }
@@ -59,17 +58,17 @@ function resolveDefaultDbPath({ cwd, existsSync, systemDbPath }) {
59
58
  return systemDbPath;
60
59
  }
61
60
 
62
- function guardImplicitDefaultUpgrade({ cwd, existsSync, systemDbPath }) {
61
+ function guardImplicitDefaultUpgrade({ cwd, existsSync }) {
63
62
  const legacyPath = resolve(cwd, '.fuli/context.json');
64
63
  const localDatabasePath = resolve(cwd, LEGACY_LOCAL_DB_PATH);
65
64
  if (existsSync(legacyPath) && !existsSync(localDatabasePath)) {
66
- throw new RuntimeConfigurationError(migrationMessage(legacyPath, systemDbPath));
65
+ throw new RuntimeConfigurationError(legacyJsonMessage(legacyPath));
67
66
  }
68
67
  }
69
68
 
70
- function migrationMessage(source, destination) {
71
- return 'JSON is a legacy import format. Run once: node src/cli.js migrate ' +
72
- `--from ${quoteShellArgument(source)} --to ${quoteShellArgument(destination)}`;
69
+ function legacyJsonMessage(source) {
70
+ return `JSON is a retired legacy format (${quoteShellArgument(source)}). ` +
71
+ 'The current Fuli CLI no longer provides SQLite knowledge commands or JSON migration.';
73
72
  }
74
73
 
75
74
  export function openLocalApplication({ dbPath, personalSpaceName = DEFAULT_PERSONAL_SPACE }) {
@@ -2,6 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
 
3
3
  import { replaceFuliTable } from './codex-config.js';
4
4
  import { isCodexBootstrapCurrent } from './codex-bootstrap.js';
5
+ import { hasCurrentClaudeCodeHooks } from './claude-code-config.js';
5
6
  import { isSessionSkillCurrent } from './session-skill.js';
6
7
  import { readJsonFile } from '../storage/json-file.js';
7
8
 
@@ -27,9 +28,16 @@ export function inspectAgentInstallations(agents, context, {
27
28
  context,
28
29
  { fileExists, readText }
29
30
  );
31
+ const claudeLifecycleCurrent = agent.id !== 'claude-code' ||
32
+ hasCurrentClaudeCodeHooks(readJson(agent.settingsPath));
30
33
 
31
34
  let integrationStatus = 'not_connected';
32
- if (registration.current && skillsCurrent && codexBootstrapCurrent) {
35
+ if (
36
+ registration.current
37
+ && skillsCurrent
38
+ && codexBootstrapCurrent
39
+ && claudeLifecycleCurrent
40
+ ) {
33
41
  integrationStatus = 'connected';
34
42
  }
35
43
  else if (registration.present || skillsPresent) integrationStatus = 'update_available';
@@ -42,6 +50,9 @@ export function inspectAgentInstallations(agents, context, {
42
50
  skills: skillsCurrent ? 'current' : skillsPresent ? 'outdated' : 'missing',
43
51
  ...(agent.id === 'codex'
44
52
  ? { bootstrap: codexBootstrapCurrent ? 'current' : 'outdated' }
53
+ : {}),
54
+ ...(agent.id === 'claude-code'
55
+ ? { lifecycleHooks: claudeLifecycleCurrent ? 'current' : 'outdated' }
45
56
  : {})
46
57
  }
47
58
  };
@@ -71,7 +82,8 @@ function inspectRegistration(agent, context, readers) {
71
82
  present,
72
83
  current: present &&
73
84
  server.command === expected.command &&
74
- sameStrings(server.args, expected.args)
85
+ sameStrings(server.args, expected.args) &&
86
+ (agent.id !== 'claude-code' || server.alwaysLoad === true)
75
87
  };
76
88
  } catch {
77
89
  return { present: false, current: false };
@@ -3,6 +3,10 @@ import { existsSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import path from 'node:path';
5
5
 
6
+ import {
7
+ connectClaudeCode,
8
+ disconnectClaudeCode
9
+ } from './claude-code-config.js';
6
10
  import { connectCursor, disconnectCursor } from './cursor-config.js';
7
11
  import { connectCodex, disconnectCodex } from './codex-config.js';
8
12
 
@@ -34,6 +38,7 @@ export function discoverAgents({
34
38
  label: 'Claude Code',
35
39
  command: 'claude',
36
40
  configPath: pathApi.join(homeDir, '.claude.json'),
41
+ settingsPath: pathApi.join(homeDir, '.claude', 'settings.json'),
37
42
  skillPath: pathApi.join(homeDir, '.claude', 'skills', skillName),
38
43
  projectSkillPath: pathApi.join(homeDir, '.claude', 'skills', projectSkillName),
39
44
  available: Boolean(commandExists('claude'))
@@ -75,9 +80,13 @@ export function buildAgentCommands(agent, context) {
75
80
  export function connectAgent(agent, context, {
76
81
  runCommand = defaultRunCommand,
77
82
  connectCodexConfig = connectCodex,
83
+ connectClaudeCodeConfig = connectClaudeCode,
78
84
  connectCursorConfig = connectCursor
79
85
  } = {}) {
80
86
  if (agent.id === 'codex') return connectCodexConfig(agent, context);
87
+ if (agent.id === 'claude-code') {
88
+ return connectClaudeCodeConfig(agent, context);
89
+ }
81
90
  if (agent.id === 'cursor') return connectCursorConfig(agent, context);
82
91
 
83
92
  const commands = buildAgentCommands(agent, context);
@@ -91,10 +100,12 @@ export function connectAgent(agent, context, {
91
100
 
92
101
  export function disconnectAgent(agent, {
93
102
  disconnectCodexConfig = disconnectCodex,
103
+ disconnectClaudeCodeConfig = disconnectClaudeCode,
94
104
  disconnectJsonConfig = disconnectCursor
95
105
  } = {}) {
96
106
  if (agent.id === 'codex') return disconnectCodexConfig(agent);
97
- if (agent.id === 'cursor' || agent.id === 'claude-code') {
107
+ if (agent.id === 'claude-code') return disconnectClaudeCodeConfig(agent);
108
+ if (agent.id === 'cursor') {
98
109
  return disconnectJsonConfig(agent);
99
110
  }
100
111
  throw new TypeError(`Unsupported agent: ${agent.id}`);
@@ -0,0 +1,194 @@
1
+ import { existsSync } from 'node:fs';
2
+
3
+ import {
4
+ readJsonFile,
5
+ writeJsonFileAtomic
6
+ } from '../storage/json-file.js';
7
+
8
+ const FULI_SERVER = 'fuli';
9
+ const MANAGED_HOOK_TOOLS = new Set([
10
+ 'begin_task_context',
11
+ 'verify_task_checkpoint'
12
+ ]);
13
+
14
+ export function connectClaudeCode(agent, context, {
15
+ readConfig = readJsonFile,
16
+ writeConfig = writeJsonFileAtomic
17
+ } = {}) {
18
+ const current = readConfig(agent.configPath, {});
19
+ const settings = readConfig(agent.settingsPath, {});
20
+ assertObject(current, 'Claude Code config');
21
+ assertObject(settings, 'Claude Code settings');
22
+ assertObject(current.mcpServers ?? {}, 'Claude Code mcpServers');
23
+ assertHookSettings(settings.hooks ?? {});
24
+
25
+ const next = {
26
+ ...current,
27
+ mcpServers: {
28
+ ...(current.mcpServers ?? {}),
29
+ [FULI_SERVER]: {
30
+ type: 'stdio',
31
+ command: context.nodePath,
32
+ args: [
33
+ context.mcpServerPath,
34
+ '--runtime-config',
35
+ context.runtimeConfigPath
36
+ ],
37
+ alwaysLoad: true
38
+ }
39
+ }
40
+ };
41
+ const nextSettings = withManagedHooks(settings);
42
+ const registrationChanged = !sameJson(current, next);
43
+ const hooksChanged = !sameJson(settings, nextSettings);
44
+ if (registrationChanged) writeConfig(agent.configPath, next);
45
+ if (hooksChanged) writeConfig(agent.settingsPath, nextSettings);
46
+
47
+ return {
48
+ id: agent.id,
49
+ label: agent.label,
50
+ status: 'connected',
51
+ newTaskRequired: registrationChanged || hooksChanged
52
+ };
53
+ }
54
+
55
+ export function disconnectClaudeCode(agent, {
56
+ fileExists = existsSync,
57
+ readConfig = readJsonFile,
58
+ writeConfig = writeJsonFileAtomic
59
+ } = {}) {
60
+ let changed = false;
61
+ if (fileExists(agent.configPath)) {
62
+ const current = readConfig(agent.configPath, {});
63
+ assertObject(current, 'Claude Code config');
64
+ assertObject(current.mcpServers ?? {}, 'Claude Code mcpServers');
65
+ if (Object.hasOwn(current.mcpServers ?? {}, FULI_SERVER)) {
66
+ const { [FULI_SERVER]: _removed, ...remainingServers } =
67
+ current.mcpServers;
68
+ writeConfig(agent.configPath, {
69
+ ...current,
70
+ mcpServers: remainingServers
71
+ });
72
+ changed = true;
73
+ }
74
+ }
75
+
76
+ if (agent.settingsPath && fileExists(agent.settingsPath)) {
77
+ const settings = readConfig(agent.settingsPath, {});
78
+ assertObject(settings, 'Claude Code settings');
79
+ assertHookSettings(settings.hooks ?? {});
80
+ const nextSettings = withoutManagedHooks(settings);
81
+ if (!sameJson(settings, nextSettings)) {
82
+ writeConfig(agent.settingsPath, nextSettings);
83
+ changed = true;
84
+ }
85
+ }
86
+
87
+ return {
88
+ id: agent.id,
89
+ label: agent.label,
90
+ status: changed ? 'disconnected' : 'not_connected'
91
+ };
92
+ }
93
+
94
+ export function hasCurrentClaudeCodeHooks(settings) {
95
+ try {
96
+ assertObject(settings, 'Claude Code settings');
97
+ assertHookSettings(settings.hooks ?? {});
98
+ } catch {
99
+ return false;
100
+ }
101
+ return [
102
+ ['UserPromptSubmit', 'begin_task_context'],
103
+ ['Stop', 'verify_task_checkpoint']
104
+ ].every(([event, tool]) => (
105
+ (settings.hooks?.[event] ?? []).some((group) =>
106
+ group.hooks.some((hook) => isManagedHook(hook, tool))
107
+ )
108
+ ));
109
+ }
110
+
111
+ function withManagedHooks(settings) {
112
+ const clean = withoutManagedHooks(settings);
113
+ const hooks = { ...(clean.hooks ?? {}) };
114
+ hooks.UserPromptSubmit = [
115
+ ...(hooks.UserPromptSubmit ?? []),
116
+ managedHookGroup('begin_task_context', {
117
+ sessionId: '${session_id}',
118
+ projectPath: '${cwd}'
119
+ }, 'Loading Fuli task context')
120
+ ];
121
+ hooks.Stop = [
122
+ ...(hooks.Stop ?? []),
123
+ managedHookGroup(
124
+ 'verify_task_checkpoint',
125
+ { sessionId: '${session_id}' },
126
+ 'Checking Fuli task checkpoint'
127
+ )
128
+ ];
129
+ return { ...clean, hooks };
130
+ }
131
+
132
+ function withoutManagedHooks(settings) {
133
+ if (!settings.hooks) return settings;
134
+ const hooks = {};
135
+ for (const [event, groups] of Object.entries(settings.hooks)) {
136
+ const remainingGroups = groups
137
+ .map((group) => ({
138
+ ...group,
139
+ hooks: group.hooks.filter((hook) => !isManagedHook(hook))
140
+ }))
141
+ .filter((group) => group.hooks.length > 0);
142
+ if (remainingGroups.length) hooks[event] = remainingGroups;
143
+ }
144
+ if (Object.keys(hooks).length === 0) {
145
+ const { hooks: _removed, ...withoutHooks } = settings;
146
+ return withoutHooks;
147
+ }
148
+ return { ...settings, hooks };
149
+ }
150
+
151
+ function managedHookGroup(tool, input, statusMessage) {
152
+ return {
153
+ hooks: [{
154
+ type: 'mcp_tool',
155
+ server: FULI_SERVER,
156
+ tool,
157
+ input,
158
+ timeout: 30,
159
+ statusMessage
160
+ }]
161
+ };
162
+ }
163
+
164
+ function isManagedHook(hook, tool = null) {
165
+ return hook?.type === 'mcp_tool'
166
+ && hook.server === FULI_SERVER
167
+ && MANAGED_HOOK_TOOLS.has(hook.tool)
168
+ && (tool === null || hook.tool === tool);
169
+ }
170
+
171
+ function assertHookSettings(value) {
172
+ assertObject(value, 'Claude Code hooks');
173
+ for (const [event, groups] of Object.entries(value)) {
174
+ if (!Array.isArray(groups)) {
175
+ throw new TypeError(`Claude Code hook event ${event} must be an array`);
176
+ }
177
+ for (const group of groups) {
178
+ assertObject(group, `Claude Code ${event} hook group`);
179
+ if (!Array.isArray(group.hooks)) {
180
+ throw new TypeError(`Claude Code ${event} hook group must contain hooks`);
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ function assertObject(value, label) {
187
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
188
+ throw new TypeError(`${label} must be a JSON object`);
189
+ }
190
+ }
191
+
192
+ function sameJson(left, right) {
193
+ return JSON.stringify(left) === JSON.stringify(right);
194
+ }
@@ -12,11 +12,26 @@ export function backupAgentConfig(agent, {
12
12
  makeDirectory = mkdirSync,
13
13
  copyFile = copyFileSync
14
14
  }) {
15
- if (!fileExists(agent.configPath)) return null;
15
+ const sources = [
16
+ { path: agent.configPath, suffix: '' },
17
+ ...(agent.settingsPath
18
+ ? [{ path: agent.settingsPath, suffix: '-settings' }]
19
+ : [])
20
+ ].filter(({ path }) => path && fileExists(path));
21
+ if (sources.length === 0) return null;
16
22
  makeDirectory(backupDir, { recursive: true });
17
23
  const timestamp = now().toISOString().replaceAll(':', '-').replaceAll('.', '-');
18
- const extension = extname(agent.configPath) || '.bak';
19
- const backupPath = join(backupDir, `${agent.id}-${timestamp}${extension}`);
20
- copyFile(agent.configPath, backupPath);
21
- return backupPath;
24
+ const backups = sources.map(({ path, suffix }) => {
25
+ const extension = extname(path) || '.bak';
26
+ const backupPath = join(
27
+ backupDir,
28
+ `${agent.id}${suffix}-${timestamp}${extension}`
29
+ );
30
+ copyFile(path, backupPath);
31
+ return { path, backupPath };
32
+ });
33
+ return (
34
+ backups.find(({ path }) => path === agent.configPath)
35
+ ?? backups[0]
36
+ ).backupPath;
22
37
  }
@@ -1 +0,0 @@
1
- import{D as e,L as t,P as n,T as r,a as i,c as a,d as o,m as s,n as c,nt as l,p as u,t as d,u as f,v as p,y as m}from"./console-tbvTUi6p.js";import{o as h}from"./runtime-dom.esm-bundler-DttsK21F.js";import{n as g,r as _,t as v}from"./identity-B3s5CXhv.js";var y={class:`view connections-view`},b={class:`service-connection-grid`,"aria-label":`知识服务连接状态`},x=[`data-status`],S={class:`service-state`},C=[`data-status`],w={class:`service-state`},T={key:0,class:`connection-subscriptions`},E={class:`subscription-list`},D=[`onClick`],O={key:0,class:`empty-state`},k=m({__name:`ConnectionsPage`,setup(m){let k=d(),A=n(``),j=f(()=>k.state?.providers?.personal?.status===`ready`),M=f(()=>k.state?.providers?.workspaces??[]),N=f(()=>M.value.filter(({status:e})=>e===`ready`).length),P=f(()=>k.state?.subscriptions??[]),F=f(()=>new Set(P.value.map(({provider_url:e,project_id:t})=>`${e}::${t}`))),I=f(()=>(k.state?.projects??[]).filter(e=>!F.value.has(R(e)))),L=f(()=>I.value.map(e=>({value:R(e),label:e.name,meta:`#${v(e.id,26)}`,search:g(e.id)})));function R(e){return`${e.providerUrl}::${e.id}`}async function z(){let e=I.value.find(e=>R(e)===A.value);if(e)try{await i(`/api/subscriptions`,{personalSpaceId:k.activePersonalSpace?.id,projectId:e.id,providerUrl:e.providerUrl,projectName:e.name}),A.value=``,k.notify(`已订阅“${e.name}”。`),await k.refresh()}catch(e){k.reportError(e)}}async function B(e){try{let t=new URLSearchParams({personalSpaceId:k.activePersonalSpace?.id??``,providerUrl:e.provider_url});await c(`/api/subscriptions/${encodeURIComponent(e.project_id)}?${t}`),k.notify(`已取消订阅“${e.project_name??e.project_id}”。`),await k.refresh()}catch(e){k.reportError(e)}}return(n,i)=>(r(),s(`section`,y,[i[12]||=o(`div`,{class:`connection-intro`},[o(`h3`,null,`服务连接与订阅`),o(`p`,null,`本地知识库始终独立运行;公共能力只在连接共享 Provider 后开启。`)],-1),o(`div`,b,[o(`article`,{class:`service-connection-card`,"data-status":j.value?`ready`:`error`},[o(`header`,null,[i[1]||=o(`span`,{class:`service-connection-icon local`,"aria-hidden":`true`},[o(`span`,{class:`nav-icon nav-icon-personal-project`})],-1),i[2]||=o(`div`,null,[o(`p`,null,`LOCAL KNOWLEDGE`),o(`h3`,null,`本地 Graphiti`)],-1),o(`span`,S,l(j.value?`已连接`:`连接异常`),1)]),o(`p`,null,l(j.value?`个人项目、协作偏好和会话知识正在写入本机图谱。`:`本地知识库暂时无法使用,请检查 Graphiti 与 Neo4j。`),1),i[3]||=o(`dl`,null,[o(`div`,null,[o(`dt`,null,`存储`),o(`dd`,null,`Neo4j`)]),o(`div`,null,[o(`dt`,null,`用途`),o(`dd`,null,`个人项目与协作偏好`)])],-1)],8,x),o(`article`,{class:`service-connection-card`,"data-status":t(k).publicRuntimeStatus},[o(`header`,null,[i[4]||=o(`span`,{class:`service-connection-icon public`,"aria-hidden":`true`},[o(`span`,{class:`nav-icon nav-icon-public-project`})],-1),i[5]||=o(`div`,null,[o(`p`,null,`PUBLIC PROVIDER`),o(`h3`,null,`公共服务`)],-1),o(`span`,w,l(t(k).publicRuntimeStatus===`ready`?`已连接`:t(k).publicRuntimeStatus===`error`?`连接异常`:`未连接`),1)]),o(`p`,null,l(t(k).publicRuntimeStatus===`ready`?`公共项目、订阅、发布与团队协作功能已经可以使用。`:t(k).publicRuntimeStatus===`error`?`公共服务已经配置,但当前无法访问;本地知识库仍可正常使用。`:`当前未连接公共服务;公共能力保持关闭。`),1),o(`dl`,null,[o(`div`,null,[i[6]||=o(`dt`,null,`当前状态`,-1),o(`dd`,null,l(t(k).publicRuntimeStatus===`ready`?`${N.value||M.value.length} 个共享服务可用`:`尚未可用`),1)]),i[7]||=o(`div`,null,[o(`dt`,null,`用途`),o(`dd`,null,`发现、订阅、发布与协作`)],-1)])],8,C)]),t(k).state?.capabilities?.subscribeProject?(r(),s(`section`,T,[i[11]||=o(`div`,{class:`section-title`},[o(`h3`,null,`公共项目订阅`),o(`p`,null,`只把你主动选择的公共项目加入个人检索范围。`)],-1),o(`div`,E,[(r(!0),s(a,null,e(P.value,e=>(r(),s(`div`,{key:`${e.provider_url}:${e.project_id}`,class:`subscription-row`},[i[9]||=o(`span`,null,`↗`,-1),o(`div`,null,[o(`strong`,null,l(e.project_name??e.project_id),1),i[8]||=o(`span`,null,`团队共享项目`,-1)]),o(`button`,{class:`secondary-action subscription-action`,type:`button`,onClick:t=>B(e)},`取消订阅`,8,D)]))),128)),P.value.length?u(``,!0):(r(),s(`div`,O,`尚未订阅团队共享项目`))]),o(`form`,{class:`subscription-form`,onSubmit:h(z,[`prevent`])},[p(_,{modelValue:A.value,"onUpdate:modelValue":i[0]||=e=>A.value=e,options:L.value,label:`团队共享项目`,placeholder:`选择公共项目`,searchable:``,required:``},null,8,[`modelValue`,`options`]),i[10]||=o(`button`,{type:`submit`},`订阅项目`,-1)],32)])):u(``,!0)]))}});export{k as default};
@@ -1 +0,0 @@
1
- import{A as e,D as t,L as n,M as r,N as i,P as a,T as o,_ as s,a as c,c as l,d as u,i as d,m as f,nt as p,p as m,t as h,tt as g,u as _,v,y}from"./console-tbvTUi6p.js";import{a as b,n as x,o as S}from"./runtime-dom.esm-bundler-DttsK21F.js";import{n as C,r as w,t as T}from"./identity-B3s5CXhv.js";import{C as E,D,E as O,S as k,T as A,d as j,f as M,g as N,h as P,i as F,l as I,r as L,s as R,u as z,w as B,x as V}from"./model-DVT8sjy9.js";var H=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},U={key:0,open:``,class:`knowledge-confirm-dialog vue-dialog`},W={class:`project-dialog-header`},G=[`disabled`],K={class:`knowledge-confirm-summary`},q={class:`knowledge-confirm-fields`},J={class:`knowledge-confirm-acknowledgement`},Y={key:0,class:`publish-dialog-error`,role:`alert`},X={class:`project-profile-dialog-actions`},ee=[`disabled`],te=[`disabled`],ne=H(y({__name:`KnowledgeConfirmDialog`,props:{item:{},personalSpaceId:{},personalProjectId:{}},emits:[`close`,`saved`],setup(t,{emit:i}){let s=t,c=i,l=h(),g=a(``),v=a(``),y=a(``),C=a(!1),w=a(!1),T=a(``),E=a({kind:`import`,label:`历史记录`}),D=_(()=>s.item?.profileAspect?`这条偏好`:`这条知识`);e(()=>s.item,e=>{if(!e)return;let t=e.evidence.at(0),n=e.confirmationBasis;g.value=n?.existence_reason||t?.source_description||t?.summary||`该内容由已保留的来源记录支持。`,v.value=n?.quadrant_reason||e.reasoningSummary||`该内容在发现时符合“${A(e.originQuadrant)}”:${B(e.originQuadrant)}`,E.value=n?.proposed_by??k(t),y.value=`已核对内容与发现时象限,确认无误。`,C.value=!1,T.value=``},{immediate:!0});async function O(){let e=s.item;if(!(!e||w.value)){if(!e.classificationExplicit)return j(`请先补充发现时象限,再确认这条知识`);if(!g.value.trim())return j(`请说明为什么会有这条知识`);if(!v.value.trim())return j(`请说明为什么属于当前象限`);if(!y.value.trim())return j(`请填写确认说明`);if(!C.value)return j(`请先确认已经核对内容与发现时象限`);w.value=!0,T.value=``;try{await d(`/api/knowledge/${e.itemKind}/${encodeURIComponent(e.id)}`,{personalSpaceId:s.personalSpaceId,personalProjectId:s.personalProjectId,action:`confirm`,reason:y.value.trim(),confirmationStatus:`confirmed`,confirmationBasis:{existenceReason:g.value.trim(),quadrantReason:v.value.trim(),proposedBy:E.value,confirmedBy:{kind:`user`,label:`当前用户`},confirmedAt:new Date().toISOString()}}),l.notify(`${D.value}已确认,并记录了确认人和确认时间。`),c(`saved`),c(`close`)}catch(e){T.value=e instanceof Error?e.message:`确认失败`,l.reportError(e)}finally{w.value=!1}}}function k(e){let t={codex:`Codex`,claude_code:`Claude Code`,cursor:`Cursor`,kiro:`Kiro`,other:`其他 Agent`}[e?.source_application??``];return t?{kind:`agent`,label:t}:{kind:`import`,label:`历史记录`}}function j(e){T.value=e}return(e,i)=>t.item?(o(),f(`dialog`,U,[u(`form`,{class:`knowledge-confirm-dialog-shell`,onSubmit:S(O,[`prevent`])},[u(`header`,W,[u(`div`,null,[i[6]||=u(`p`,{class:`eyebrow`},`KNOWLEDGE CONFIRMATION`,-1),u(`h3`,null,`确认`+p(D.value),1),i[7]||=u(`p`,null,`确认表示你已核对内容与发现时象限;系统会记录当前用户和确认时间。`,-1)]),u(`button`,{class:`secondary-action`,type:`button`,disabled:w.value,onClick:i[0]||=e=>c(`close`)},` 关闭 `,8,G)]),u(`section`,K,[u(`span`,null,p(t.item.profileAspect?`协作偏好`:`知识内容`),1),u(`strong`,null,p(t.item.title),1),u(`p`,null,p(t.item.body),1),u(`small`,null,`发现时象限 · `+p(n(A)(t.item.originQuadrant)),1)]),u(`div`,q,[u(`label`,null,[i[8]||=u(`span`,null,`为什么会有这条知识`,-1),r(u(`textarea`,{"onUpdate:modelValue":i[1]||=e=>g.value=e,name:`confirmation-existence-reason`,maxlength:`4096`,rows:`3`,required:``},null,512),[[b,g.value]])]),u(`label`,null,[i[9]||=u(`span`,null,`为什么属于当前象限`,-1),r(u(`textarea`,{"onUpdate:modelValue":i[2]||=e=>v.value=e,name:`confirmation-quadrant-reason`,maxlength:`4096`,rows:`3`,required:``},null,512),[[b,v.value]])]),u(`label`,null,[i[10]||=u(`span`,null,`确认说明`,-1),r(u(`textarea`,{"onUpdate:modelValue":i[3]||=e=>y.value=e,name:`confirmation-reason`,maxlength:`2000`,rows:`2`,required:``},null,512),[[b,y.value]])])]),u(`label`,J,[r(u(`input`,{"onUpdate:modelValue":i[4]||=e=>C.value=e,name:`confirmation-acknowledged`,type:`checkbox`},null,512),[[x,C.value]]),i[11]||=u(`span`,null,`我已核对这条内容及其发现时象限,并确认上述依据准确。`,-1)]),T.value?(o(),f(`p`,Y,p(T.value),1)):m(``,!0),u(`footer`,X,[u(`button`,{class:`secondary-action`,type:`button`,disabled:w.value,onClick:i[5]||=e=>c(`close`)},` 取消 `,8,ee),u(`button`,{class:`primary-action`,type:`submit`,disabled:w.value||!C.value},p(w.value?`正在确认…`:`确认${D.value}`),9,te)])],32)])):m(``,!0)}}),[[`__scopeId`,`data-v-65eb248e`]]),re={key:0,open:``,class:`project-dialog knowledge-edit-dialog vue-dialog`},ie={class:`project-dialog-shell`},ae={class:`project-dialog-header`},oe={class:`knowledge-edit-columns`},se={key:0},ce={key:1},le={key:2},ue={class:`knowledge-taxonomy-fields`},de={class:`knowledge-classification-warning`},fe={key:3,class:`knowledge-classification-warning`},pe={class:`knowledge-confirmation-fields`},me={class:`knowledge-taxonomy-fields`},he={key:4,class:`knowledge-replacement-fields`},ge={key:0},_e={class:`knowledge-replacement-fields`},ve={key:5,class:`publish-dialog-error`,role:`alert`},ye={class:`knowledge-editor-actions`},be=[`disabled`],xe=[`disabled`],Se=[`disabled`],Ce=[`disabled`],we={key:0},Te=[`disabled`],Ee={key:1},De={key:0},Oe=[`disabled`],ke=y({__name:`KnowledgeEditDialog`,props:{item:{},personalSpaceId:{},personalProjectId:{},projects:{},replacementItems:{default:()=>[]}},emits:[`close`,`saved`],setup(t,{emit:g}){let y=t,x=g,E=h(),D=a(!1),O=a(``),k=a(``),j=a(``),M=a(`global`),N=a(``),P=a(``),F=a(``),I=a(``),L=i({name:``,summary:``,fact:``,currentQuadrant:``,confirmationStatus:`pending`,existenceReason:``,quadrantReason:``,proposedByKind:`agent`,proposedByLabel:``,confirmedByKind:`user`,confirmedByLabel:``,profileAspect:`none`,inheritanceMode:`local_only`,reason:``}),R=_(()=>y.item?.itemKind===`relationship`),z=_(()=>!!y.item?.invalidAt),B=_(()=>!!y.item?.raw.profile_aspect),V=_(()=>y.projects.map(e=>({value:e.project_id,label:e.profile.name,meta:`#${T(e.project_id,26)}`,search:C(e.project_id)}))),H=[{value:`known_known`,label:`已知的已知 · 被明确表达的知识或结论`},{value:`known_unknown`,label:`已知的未知 · 被明确提出的未解问题`},{value:`unknown_known`,label:`未知的已知 · 从行为或反馈提炼的隐性知识`},{value:`unknown_unknown`,label:`未知的未知 · 探索中发现的潜在盲点`}],U=[{value:`pending`,label:`待确认 · 尚未完成内容与象限审核`},{value:`confirmed`,label:`已确认 · 内容与象限归类均已确认`}],W=[{value:`local_only`,label:`仅当前项目 · 不自动借给其他项目`},{value:`descendants`,label:`子项目可继承 · 仅沿明确知识关系`},{value:`selected_projects`,label:`仅指定项目可继承`}],G=[{value:`user`,label:`用户`},{value:`agent`,label:`Agent`},{value:`authoritative_source`,label:`权威来源`},{value:`import`,label:`导入记录`}],K=[{value:`user`,label:`用户`},{value:`authoritative_source`,label:`权威来源`}],q=[{value:`none`,label:`不属于协作偏好`},{value:`taste`,label:`品味`},{value:`personality`,label:`个性`},{value:`judgment_preference`,label:`判断偏好`}],J=_(()=>[{value:`global`,label:`个人全局`},{value:`project`,label:`指定个人项目`,disabled:y.projects.length===0}]),Y=_(()=>[{value:``,label:`没有明确的替代内容`},...y.replacementItems.filter(e=>!e.invalidAt&&(e.itemKind!==y.item?.itemKind||e.id!==y.item?.id)).map(e=>({value:Me(e.itemKind,e.id),label:e.title,meta:e.type,search:`${e.body} ${e.id}`}))]),X=_(()=>{if(!I.value)return null;try{let[e,t]=JSON.parse(I.value);return e!==`entity`&&e!==`relationship`||typeof t!=`string`?null:{itemKind:e,id:t}}catch{return null}}),ee=_(()=>y.item?.assignments.at(0)?.project_id??y.item?.evidence.find(({personal_project_id:e})=>e)?.personal_project_id??y.personalProjectId??null);e(()=>y.item,e=>{if(!e)return;let t=e.raw;L.name=e.itemKind===`entity`?t.name:``,L.summary=e.itemKind===`entity`?t.summary??``:``,L.fact=e.itemKind===`relationship`?t.fact??``:``;let n=e.confirmationBasis,r=e.evidence.at(0);L.currentQuadrant=e.classificationExplicit?t.current_quadrant??t.origin_quadrant??`known_known`:``,L.confirmationStatus=e.confirmationStatus===`confirmed`?`confirmed`:`pending`,L.existenceReason=n?.existence_reason??r?.source_description??r?.summary??``,L.quadrantReason=n?.quadrant_reason??t.reasoning_summary??``,L.proposedByKind=n?.proposed_by.kind??`agent`,L.proposedByLabel=n?.proposed_by.label??``,L.confirmedByKind=n?.confirmed_by?.kind??`user`,L.confirmedByLabel=n?.confirmed_by?.label??``,L.profileAspect=t.profile_aspect??`none`,L.inheritanceMode=t.profile_aspect?`local_only`:t.inheritance_mode??`local_only`,F.value=t.inherited_project_ids?.at(0)??y.projects[0]?.project_id??``,L.reason=``,k.value=``,j.value=ee.value??y.projects[0]?.project_id??``,M.value=t.preference_scope??`global`,N.value=t.preference_project_id??y.projects[0]?.project_id??``,P.value=``,I.value=e.replacedByItemId&&e.replacedByItemKind?Me(e.replacedByItemKind,e.replacedByItemId):``,O.value=``},{immediate:!0});async function te(){let e=y.item;if(!e)return;if(!L.reason.trim())return $(`请说明纠正原因`);if(!R.value&&!L.name.trim())return $(`名称不能为空`);if(R.value&&!L.fact.trim())return $(`关系事实不能为空`);if(!L.currentQuadrant||!L.confirmationStatus)return $(`请先明确当前分类与确认状态`);if(!L.existenceReason.trim())return $(`请说明为什么会有这条知识`);if(!L.quadrantReason.trim())return $(`请说明为什么被分到这个象限`);if(!L.proposedByKind)return $(`请选择提出者`);if(L.confirmationStatus===`confirmed`&&!L.confirmedByKind)return $(`已确认状态必须记录确认者`);if(L.inheritanceMode===`selected_projects`&&!F.value)return $(`请选择可以继承这条知识的项目`);let t={...Z(`update`),confirmationStatus:L.confirmationStatus,confirmationBasis:{existenceReason:L.existenceReason.trim(),quadrantReason:L.quadrantReason.trim(),proposedBy:{kind:L.proposedByKind,label:L.proposedByLabel.trim()||null},confirmedBy:L.confirmationStatus===`confirmed`?{kind:L.confirmedByKind,label:L.confirmedByLabel.trim()||null}:null,confirmedAt:L.confirmationStatus===`confirmed`?new Date().toISOString():null},profileAspect:L.profileAspect,inheritanceMode:L.profileAspect===`none`?L.inheritanceMode:`local_only`,inheritedProjectIds:L.profileAspect===`none`&&L.inheritanceMode===`selected_projects`?[F.value]:[],reasoningSummary:L.quadrantReason.trim()};y.item.classificationExplicit?t.currentQuadrant=L.currentQuadrant:t.originQuadrant=L.currentQuadrant,R.value?t.fact=L.fact.trim():(t.name=L.name.trim(),t.summary=L.summary.trim()),await Q(async()=>{await d(`/api/knowledge/${e.itemKind}/${encodeURIComponent(e.id)}`,t),E.notify(`知识已纠正,原始证据仍然保留。`)})}async function ne(e){let t=y.item;if(t){if(!L.reason.trim())return $(e===`invalidate`?`请说明为什么这条知识已经失效`:`请说明为什么恢复有效`);await Q(async()=>{let n=Z(e);e===`invalidate`&&X.value&&(n.replacementItemId=X.value.id,n.replacementItemKind=X.value.itemKind),await d(`/api/knowledge/${t.itemKind}/${encodeURIComponent(t.id)}`,n),E.notify(e===`invalidate`?X.value?`知识已标记为失效,并已关联替代内容。`:`知识已标记为失效,历史记录已保留。`:`知识已恢复为有效。`)})}}async function ke(){let e=y.item,t=X.value;if(!(!e||!z.value)){if(!t)return $(`请选择明确的替代内容`);if(!L.reason.trim())return $(`请说明补充替代关联的依据`);await Q(async()=>{await d(`/api/knowledge/${e.itemKind}/${encodeURIComponent(e.id)}`,{...Z(`link_replacement`),replacementItemId:t.id,replacementItemKind:t.itemKind}),E.notify(`替代关联已保存,现在可以从历史记录直接跳转。`)})}}async function Ae(){let e=y.item;if(e){if(!j.value)return $(`请选择目标个人项目`);if(!k.value.trim())return $(`请说明调整原因`);if(j.value===ee.value)return $(`这条知识已经属于该项目`);await Q(async()=>{await c(`/api/knowledge/${e.itemKind}/${encodeURIComponent(e.id)}/assignment`,{personalSpaceId:y.personalSpaceId,targetProjectId:j.value,reason:k.value.trim()}),E.notify(`项目归属已调整,来源会话和历史证据没有改变。`)})}}async function je(){let e=y.item;if(!e)return;let t=M.value===`project`?N.value:null;if(M.value===`project`&&!t)return $(`请选择生效的个人项目`);if(!P.value.trim())return $(`请说明为什么调整生效范围`);await Q(async()=>{await c(`/api/knowledge/${e.itemKind}/${encodeURIComponent(e.id)}/preference-scope`,{personalSpaceId:y.personalSpaceId,scope:M.value,projectId:t,reason:P.value.trim()}),E.notify(M.value===`global`?`这条协作偏好现在对所有个人项目生效。`:`这条协作偏好现在只对所选个人项目生效。`)})}function Z(e){return{personalSpaceId:y.personalSpaceId,personalProjectId:y.personalProjectId,action:e,reason:L.reason.trim()}}function Me(e,t){return JSON.stringify([e,t])}async function Q(e){D.value=!0,O.value=``;try{await e(),x(`close`),x(`saved`)}catch(e){O.value=e instanceof Error?e.message:`保存失败`,E.reportError(e)}finally{D.value=!1}}function $(e){O.value=e}return(e,i)=>t.item?(o(),f(`dialog`,re,[u(`div`,ie,[u(`header`,ae,[u(`div`,null,[i[24]||=u(`p`,{class:`eyebrow`},`PERSONAL KNOWLEDGE`,-1),u(`h3`,null,p(z.value?`查看和恢复知识`:`纠正知识`),1),i[25]||=u(`p`,null,`修改会新增修订历史;发现时象限保持不变,当前分类可纠正,确认状态记录审核结果。`,-1)]),u(`button`,{class:`secondary-action`,type:`button`,onClick:i[0]||=e=>x(`close`)},`关闭`)]),u(`div`,oe,[u(`section`,null,[i[46]||=u(`h4`,null,`当前内容`,-1),u(`form`,{class:`knowledge-editor-form`,onSubmit:S(te,[`prevent`])},[R.value?m(``,!0):(o(),f(`label`,se,[i[26]||=s(`名称`,-1),r(u(`input`,{"onUpdate:modelValue":i[1]||=e=>L.name=e,maxlength:`512`},null,512),[[b,L.name]])])),R.value?(o(),f(`label`,le,[i[28]||=s(`关系事实`,-1),r(u(`textarea`,{"onUpdate:modelValue":i[3]||=e=>L.fact=e,maxlength:`8192`,rows:`5`},null,512),[[b,L.fact]])])):(o(),f(`label`,ce,[i[27]||=s(`说明`,-1),r(u(`textarea`,{"onUpdate:modelValue":i[2]||=e=>L.summary=e,maxlength:`4096`,rows:`5`},null,512),[[b,L.summary]])])),u(`div`,ue,[u(`label`,null,[i[29]||=s(`当前分类 `,-1),v(w,{modelValue:L.currentQuadrant,"onUpdate:modelValue":i[4]||=e=>L.currentQuadrant=e,options:H,label:`当前分类`},null,8,[`modelValue`])]),u(`label`,null,[i[30]||=s(`确认状态 `,-1),v(w,{modelValue:L.confirmationStatus,"onUpdate:modelValue":i[5]||=e=>L.confirmationStatus=e,options:U,label:`确认状态`},null,8,[`modelValue`])]),u(`label`,null,[i[31]||=s(`协作偏好维度 `,-1),v(w,{modelValue:L.profileAspect,"onUpdate:modelValue":i[6]||=e=>L.profileAspect=e,options:q,label:`协作偏好维度`},null,8,[`modelValue`])])]),u(`p`,de,` 发现时象限:`+p(n(A)(t.item.originQuadrant))+`。这是捕获来源标签,后续编辑不会覆盖。 `,1),t.item.classificationExplicit?m(``,!0):(o(),f(`p`,fe,` 这条旧内容没有显式象限。保存前请人工选择,系统不会再自动补成“已知的已知”。 `)),u(`fieldset`,pe,[i[38]||=u(`legend`,null,`确认依据`,-1),u(`label`,null,[i[32]||=s(`为什么会有这条知识 `,-1),r(u(`textarea`,{"onUpdate:modelValue":i[7]||=e=>L.existenceReason=e,maxlength:`4096`,rows:`3`,required:``},null,512),[[b,L.existenceReason]])]),u(`label`,null,[i[33]||=s(`为什么被分到这个象限 `,-1),r(u(`textarea`,{"onUpdate:modelValue":i[8]||=e=>L.quadrantReason=e,maxlength:`4096`,rows:`3`,required:``},null,512),[[b,L.quadrantReason]])]),u(`div`,me,[u(`label`,null,[i[34]||=s(`提出者 `,-1),v(w,{modelValue:L.proposedByKind,"onUpdate:modelValue":i[9]||=e=>L.proposedByKind=e,options:G,label:`提出者`},null,8,[`modelValue`])]),u(`label`,null,[i[35]||=s(`提出者说明 `,-1),r(u(`input`,{"onUpdate:modelValue":i[10]||=e=>L.proposedByLabel=e,maxlength:`160`,placeholder:`可选,例如 Codex`},null,512),[[b,L.proposedByLabel]])]),L.confirmationStatus===`confirmed`?(o(),f(l,{key:0},[u(`label`,null,[i[36]||=s(`确认者 `,-1),v(w,{modelValue:L.confirmedByKind,"onUpdate:modelValue":i[11]||=e=>L.confirmedByKind=e,options:K,label:`确认者`},null,8,[`modelValue`])]),u(`label`,null,[i[37]||=s(`确认者说明 `,-1),r(u(`input`,{"onUpdate:modelValue":i[12]||=e=>L.confirmedByLabel=e,maxlength:`160`,placeholder:`可选,例如当前用户`},null,512),[[b,L.confirmedByLabel]])])],64)):m(``,!0)]),i[39]||=u(`p`,null,` “Agent 已确认”只能由实际使用证据策略产生;人工在这里可保留待确认,或记录用户/权威来源确认。 `,-1)]),B.value?m(``,!0):(o(),f(`fieldset`,he,[i[42]||=u(`legend`,null,`跨项目知识继承`,-1),u(`label`,null,[i[40]||=s(`继承范围 `,-1),v(w,{modelValue:L.inheritanceMode,"onUpdate:modelValue":i[13]||=e=>L.inheritanceMode=e,options:W,label:`知识继承范围`},null,8,[`modelValue`])]),L.inheritanceMode===`selected_projects`?(o(),f(`label`,ge,[i[41]||=s(`可继承项目 `,-1),v(w,{modelValue:F.value,"onUpdate:modelValue":i[14]||=e=>F.value=e,options:V.value,label:`可继承项目`,searchable:``},null,8,[`modelValue`,`options`])])):m(``,!0),i[43]||=u(`p`,null,` 只有沿 PART_OF 或 USES_KNOWLEDGE_FROM 可达时才会继承;RELATED_TO 不扩展检索范围。 `,-1)])),u(`fieldset`,_e,[u(`legend`,null,p(z.value?`替代内容`:`失效时的替代内容(可选)`),1),u(`label`,null,[s(p(z.value?`这条历史记录被哪条内容取代`:`选择当前有效的内容`)+` `,1),v(w,{modelValue:I.value,"onUpdate:modelValue":i[15]||=e=>I.value=e,options:Y.value,label:`替代内容`,searchable:``},null,8,[`modelValue`,`options`])]),i[44]||=u(`p`,null,` 只有明确选择后才会建立可跳转的替代关联;系统不会根据失效原因自动猜测。 `,-1)]),u(`label`,null,[i[45]||=s(`纠正原因`,-1),r(u(`textarea`,{"onUpdate:modelValue":i[16]||=e=>L.reason=e,maxlength:`2000`,rows:`3`,required:``},null,512),[[b,L.reason]])]),O.value?(o(),f(`p`,ve,p(O.value),1)):m(``,!0),u(`div`,ye,[z.value?(o(),f(l,{key:1},[u(`button`,{class:`secondary-action`,type:`button`,disabled:D.value,onClick:i[18]||=e=>ne(`restore`)},`恢复为有效`,8,xe),u(`button`,{class:`secondary-action`,type:`button`,disabled:D.value,onClick:ke},`保存替代关联`,8,Se)],64)):(o(),f(`button`,{key:0,class:`secondary-action`,type:`button`,disabled:D.value,onClick:i[17]||=e=>ne(`invalidate`)},`标记为失效`,8,be)),u(`button`,{class:`primary-action`,type:`submit`,disabled:D.value},p(D.value?`正在保存…`:L.confirmationStatus===`confirmed`?`保存并确认`:`保存为待确认`),9,Ce)])],32)]),B.value?(o(),f(`section`,Ee,[i[54]||=u(`h4`,null,`偏好生效范围`,-1),i[55]||=u(`p`,null,`个人全局默认跨项目生效;项目范围只对明确选择的项目生效。`,-1),u(`form`,{class:`knowledge-editor-form`,onSubmit:S(je,[`prevent`])},[u(`label`,null,[i[51]||=s(`生效范围 `,-1),v(w,{modelValue:M.value,"onUpdate:modelValue":i[21]||=e=>M.value=e,options:J.value,label:`偏好生效范围`},null,8,[`modelValue`,`options`])]),M.value===`project`?(o(),f(`label`,De,[i[52]||=s(`个人项目 `,-1),v(w,{modelValue:N.value,"onUpdate:modelValue":i[22]||=e=>N.value=e,options:V.value,label:`偏好个人项目`,searchable:``},null,8,[`modelValue`,`options`])])):m(``,!0),u(`label`,null,[i[53]||=s(`调整原因`,-1),r(u(`textarea`,{"onUpdate:modelValue":i[23]||=e=>P.value=e,maxlength:`2000`,rows:`3`,required:``},null,512),[[b,P.value]])]),u(`button`,{class:`primary-action`,type:`submit`,disabled:D.value},`保存生效范围`,8,Oe)],32)])):(o(),f(`section`,we,[i[49]||=u(`h4`,null,`项目归属`,-1),i[50]||=u(`p`,null,`只调整这条知识的项目归属,来源会话和历史证据保持不变。`,-1),u(`form`,{class:`knowledge-editor-form`,onSubmit:S(Ae,[`prevent`])},[u(`label`,null,[i[47]||=s(`目标个人项目 `,-1),v(w,{modelValue:j.value,"onUpdate:modelValue":i[19]||=e=>j.value=e,options:V.value,label:`目标个人项目`,searchable:``,required:``},null,8,[`modelValue`,`options`])]),u(`label`,null,[i[48]||=s(`调整原因`,-1),r(u(`textarea`,{"onUpdate:modelValue":i[20]||=e=>k.value=e,maxlength:`2000`,rows:`3`,required:``},null,512),[[b,k.value]])]),u(`button`,{class:`primary-action`,type:`submit`,disabled:D.value||t.projects.length===0},`调整归属`,8,Te)],32)]))])])])):m(``,!0)}}),Ae={class:`graph-inspector`},je={class:`eyebrow`},Z={class:`inspector-identity`},Me={class:`muted`},Q={key:0,class:`inspector-project-material`},$={key:3,class:`inspector-replacement`},Ne={class:`inspector-replacement-heading`},Pe={key:1},Fe={key:2},Ie={class:`inspector-meta`},Le={key:0},Re={key:1},ze={key:2},Be={key:3},Ve={key:4},He={key:5},Ue={key:6},We={key:7},Ge={key:8},Ke={key:9},qe={key:10},Je={key:12},Ye={key:0,class:`inspector-meta inspector-confirmation-basis`},Xe={key:0},Ze={key:1,class:`inspector-reasoning`},Qe={class:`inspector-meta inspector-attributes`},$e={class:`inspector-reasoning`},et={key:7,class:`inspector-actions`},tt={class:`inspector-relations`},nt={class:`relation-type`},rt={class:`relation-target`},it={class:`inspector-evidence`},at={key:0,class:`evidence-excerpt`},ot={class:`inspector-history`},st={class:`inspector-history inspector-audit-history`},ct={key:0},lt=y({__name:`KnowledgeInspector`,props:{item:{},graph:{},editable:{type:Boolean},currentProjectId:{},canPublishProject:{type:Boolean},canManageProject:{type:Boolean},mode:{}},emits:[`confirm`,`edit`,`createProject`,`openProject`,`publishProject`,`openDirectory`,`openGraph`,`openReplacement`,`manageProject`],setup(e,{emit:r}){let i=e,a=r,s=_(()=>i.item?.itemKind===`entity`?i.item.raw:null),c=_(()=>i.item?.itemKind===`relationship`?i.item.raw:null),d=_(()=>new Map(i.graph?.nodes.map(e=>[e.id,e.name])??[])),h=_(()=>!s.value||!i.graph?[]:i.graph.edges.filter(e=>R(e.source)===s.value?.id||R(e.target)===s.value?.id).slice(0,12)),v=_(()=>Object.entries(i.item?.raw.attributes??{}).filter(([,e])=>e!=null&&e!==``)),y=_(()=>j(i.item)),b=_(()=>V(i.item)),x=_(()=>i.item?.itemKind===`entity`&&[`PersonalProject`,`RelatedPersonalProject`].includes(i.item.type)),S=_(()=>i.editable&&i.item?.itemKind===`entity`&&!i.item.invalidAt&&!i.item.profileAspect&&!y.value),C=_(()=>i.editable&&!y.value),w=_(()=>i.editable&&!y.value&&!i.item?.invalidAt&&!!i.item?.classificationExplicit&&U.value===`pending`),T=_(()=>y.value&&i.canManageProject),H=_(()=>S.value||C.value||w.value||T.value||!!i.mode||x.value&&!!b.value&&b.value!==i.currentProjectId||x.value&&!!b.value&&i.canPublishProject),U=_(()=>i.item?N(i.item):`unclassified`),W=_(()=>i.item?L(i.item):``),G=_(()=>i.item?.confirmationBasis??null),K=_(()=>{let e=i.item?.replacedByItemId,t=i.item?.replacedByItemKind;return!e||!t?null:P(i.graph).find(n=>n.id===e&&n.itemKind===t)??null}),q=_(()=>{let e=i.item;return e?.humanEdited?e.humanChangeStatus===`unseen`?`这次人工修改还没有被 Agent 调用查看,提醒会持续保留。`:e.humanChangeStatus===`viewed`?`Agent 已查看当前版本,但还没有同时完成冲突检查和分类合理性审核。`:`Agent 已对当前人工修改版本完成无冲突与分类合理性审核;历史记录仍永久保留。`:``});function J(e){return typeof e==`string`?e:JSON.stringify(e)}function Y(e){return`${Math.round(e*100)}%`}function X(e){return e.inheritanceMode===`descendants`?`允许子项目继承`:e.inheritanceMode===`selected_projects`?`仅指定项目(${e.inheritedProjectIds.length})`:`仅当前项目`}return(r,i)=>(o(),f(`aside`,Ae,[e.item?(o(),f(l,{key:0},[u(`p`,je,p(y.value?`PROJECT MATERIAL`:e.item.itemKind===`entity`?`ENTITY`:`RELATIONSHIP`),1),u(`h3`,null,p(e.item.itemKind===`entity`?e.item.title:c.value?.type),1),u(`div`,Z,[u(`span`,null,p(y.value?`资料投影 ID`:e.item.itemKind===`entity`?`节点 ID`:`关系 ID`),1),u(`code`,null,p(e.item.id),1)]),u(`p`,Me,p(e.item.body),1),y.value?(o(),f(`section`,Q,[u(`div`,null,[i[9]||=u(`span`,null,`内容归属`,-1),u(`strong`,null,p(n(E)(e.item)),1)]),i[10]||=u(`p`,null,` 这是项目档案在关系图中的投影,不是可独立确认、失效或恢复的知识记录。 如需改变内容,请编辑项目资料。 `,-1)])):(o(),f(`section`,{key:1,class:g([`inspector-classification`,`state-${U.value}`])},[u(`div`,null,[i[11]||=u(`span`,null,`当前判定`,-1),u(`strong`,null,p(n(O)(e.item)),1)]),u(`p`,null,p(W.value),1)],2)),!y.value&&e.item.humanEdited?(o(),f(`section`,{key:2,class:g([`inspector-human-change`,`state-${e.item.humanChangeStatus}`])},[u(`div`,null,[i[12]||=u(`span`,null,`人工变更审核`,-1),u(`strong`,null,p(n(z)(e.item.humanChangeStatus)),1)]),u(`p`,null,p(q.value),1),u(`small`,null,`人工变更版本 `+p(e.item.humanChangeVersion),1)],2)):m(``,!0),!y.value&&e.item.invalidAt?(o(),f(`section`,$,[u(`div`,Ne,[i[13]||=u(`span`,null,`替代关系`,-1),u(`strong`,null,p(K.value?`已被以下内容取代`:`没有可跳转的替代内容`),1)]),K.value?(o(),f(`button`,{key:0,class:`inspector-replacement-link`,type:`button`,onClick:i[0]||=e=>a(`openReplacement`,K.value)},[u(`span`,null,[u(`strong`,null,p(K.value.title),1),u(`small`,null,p(K.value.body),1)]),i[14]||=u(`b`,null,`查看替代内容 →`,-1)])):e.item.replacedByItemId?(o(),f(`p`,Pe,` 已记录替代对象,但它不在当前查看范围内。 `)):(o(),f(`p`,Fe,` 这条历史记录只有失效原因,没有记录明确的替代对象;系统不会根据文字猜测链接。 `))])):m(``,!0),u(`dl`,Ie,[u(`div`,null,[i[15]||=u(`dt`,null,`类型`,-1),u(`dd`,null,p(e.item.type),1)]),y.value?m(``,!0):(o(),f(`div`,Le,[i[16]||=u(`dt`,null,`发现时象限`,-1),u(`dd`,null,p(n(A)(e.item.originQuadrant)),1)])),y.value?m(``,!0):(o(),f(`div`,Re,[i[17]||=u(`dt`,null,`当前分类`,-1),u(`dd`,null,p(n(A)(e.item.currentQuadrant)),1)])),y.value?m(``,!0):(o(),f(`div`,ze,[i[18]||=u(`dt`,null,`象限解释`,-1),u(`dd`,null,p(n(B)(e.item.originQuadrant)),1)])),y.value?m(``,!0):(o(),f(`div`,Be,[i[19]||=u(`dt`,null,`确认状态`,-1),u(`dd`,null,p(n(O)(e.item)),1)])),y.value?m(``,!0):(o(),f(`div`,Ve,[i[20]||=u(`dt`,null,`置信分`,-1),u(`dd`,null,p(Y(e.item.confidenceScore)),1)])),y.value?m(``,!0):(o(),f(`div`,He,[i[21]||=u(`dt`,null,`效用分`,-1),u(`dd`,null,p(Y(e.item.utilityScore)),1)])),y.value?m(``,!0):(o(),f(`div`,Ue,[i[22]||=u(`dt`,null,`有效使用`,-1),u(`dd`,null,p(e.item.qualifiedUseCount)+` 次 / `+p(e.item.distinctTaskCount)+` 个任务`,1)])),y.value?m(``,!0):(o(),f(`div`,We,[i[23]||=u(`dt`,null,`最近使用`,-1),u(`dd`,null,p(n(I)(e.item.lastUsedAt)),1)])),!y.value&&!e.item.profileAspect?(o(),f(`div`,Ge,[i[24]||=u(`dt`,null,`知识继承`,-1),u(`dd`,null,p(X(e.item)),1)])):m(``,!0),!y.value&&e.item.profileAspect?(o(),f(`div`,Ke,[i[25]||=u(`dt`,null,`协作偏好`,-1),u(`dd`,null,p(n(k)(e.item.profileAspect)),1)])):m(``,!0),!y.value&&e.item.profileAspect?(o(),f(`div`,qe,[i[26]||=u(`dt`,null,`生效范围`,-1),u(`dd`,null,p(e.item.preferenceScope===`project`?`指定项目`:`个人全局`),1)])):m(``,!0),c.value?(o(),f(l,{key:11},[u(`div`,null,[i[27]||=u(`dt`,null,`来源`,-1),u(`dd`,null,p(d.value.get(n(R)(c.value.source))??n(R)(c.value.source)),1)]),u(`div`,null,[i[28]||=u(`dt`,null,`目标`,-1),u(`dd`,null,p(d.value.get(n(R)(c.value.target))??n(R)(c.value.target)),1)])],64)):m(``,!0),y.value?m(``,!0):(o(),f(`div`,Je,[i[29]||=u(`dt`,null,`状态`,-1),u(`dd`,null,p(e.item.invalidAt?`历史 · 已失效`:`当前有效`),1)]))]),y.value?m(``,!0):(o(),f(l,{key:4},[i[36]||=u(`h4`,{class:`inspector-subtitle`},`确认依据`,-1),G.value?(o(),f(`dl`,Ye,[u(`div`,null,[i[30]||=u(`dt`,null,`为什么会有`,-1),u(`dd`,null,p(G.value.existence_reason),1)]),u(`div`,null,[i[31]||=u(`dt`,null,`为什么属于该象限`,-1),u(`dd`,null,p(G.value.quadrant_reason),1)]),u(`div`,null,[i[32]||=u(`dt`,null,`提出者`,-1),u(`dd`,null,p(n(F)(G.value.proposed_by)),1)]),u(`div`,null,[i[33]||=u(`dt`,null,`确认者`,-1),u(`dd`,null,p(n(F)(G.value.confirmed_by)),1)]),u(`div`,null,[i[34]||=u(`dt`,null,`确认时间`,-1),u(`dd`,null,p(n(I)(G.value.confirmed_at)),1)]),G.value.agent_policy_version?(o(),f(`div`,Xe,[i[35]||=u(`dt`,null,`Agent 策略`,-1),u(`dd`,null,p(G.value.agent_policy_version),1)])):m(``,!0)])):(o(),f(`p`,Ze,` 旧数据没有结构化的确认依据,因此已放入待确认,不会自动作为已确认知识使用。 `))],64)),v.value.length?(o(),f(l,{key:5},[i[37]||=u(`h4`,{class:`inspector-subtitle`},`内容详情`,-1),u(`dl`,Qe,[(o(!0),f(l,null,t(v.value,([e,t])=>(o(),f(`div`,{key:e},[u(`dt`,null,p(e),1),u(`dd`,null,p(J(t)),1)]))),128))])],64)):m(``,!0),e.item.reasoningSummary?(o(),f(l,{key:6},[i[38]||=u(`h4`,{class:`inspector-subtitle`},`形成过程`,-1),u(`p`,$e,p(e.item.reasoningSummary),1)],64)):m(``,!0),H.value?(o(),f(`div`,et,[w.value?(o(),f(`button`,{key:0,class:`primary-action inspector-confirm-action`,type:`button`,onClick:i[1]||=t=>a(`confirm`,e.item)},p(e.item.profileAspect?`确认这条偏好`:`确认这条知识`),1)):m(``,!0),T.value?(o(),f(`button`,{key:1,class:`primary-action`,type:`button`,onClick:i[2]||=t=>a(`manageProject`,e.item)},` 编辑项目资料 `)):m(``,!0),e.mode===`graph`?(o(),f(`button`,{key:2,class:`secondary-action`,type:`button`,onClick:i[3]||=t=>a(`openDirectory`,e.item)},` 在内容目录中定位 `)):m(``,!0),e.mode===`directory`?(o(),f(`button`,{key:3,class:`secondary-action`,type:`button`,onClick:i[4]||=t=>a(`openGraph`,e.item)},` 在关系图中定位 `)):m(``,!0),x.value&&b.value&&b.value!==e.currentProjectId?(o(),f(`button`,{key:4,class:`primary-action`,type:`button`,onClick:i[5]||=e=>a(`openProject`,b.value)},` 进入这个项目 `)):m(``,!0),x.value&&b.value&&e.canPublishProject?(o(),f(`button`,{key:5,class:`secondary-action`,type:`button`,onClick:i[6]||=e=>a(`publishProject`,b.value)},` 发布 / 同步到公共 `)):m(``,!0),S.value?(o(),f(`button`,{key:6,class:`primary-action`,type:`button`,onClick:i[7]||=t=>a(`createProject`,e.item)},` 基于此创建项目 `)):m(``,!0),C.value?(o(),f(`button`,{key:7,class:`secondary-action inspector-edit-action`,type:`button`,onClick:i[8]||=t=>a(`edit`,e.item)},p(e.item.profileAspect?`纠正这条偏好`:`纠正或调整归属`),1)):m(``,!0)])):m(``,!0),h.value.length?(o(),f(l,{key:8},[i[39]||=u(`h4`,{class:`inspector-subtitle`},`关联关系`,-1),u(`div`,tt,[(o(!0),f(l,null,t(h.value,e=>(o(),f(`div`,{key:e.id,class:`inspector-relation`},[u(`strong`,nt,p(e.type),1),u(`p`,rt,p(e.fact||`关系`),1)]))),128))])],64)):m(``,!0),e.item.evidence.length?(o(),f(l,{key:9},[i[40]||=u(`h4`,{class:`inspector-subtitle`},`证据与来源`,-1),u(`div`,it,[(o(!0),f(l,null,t(e.item.evidence,(e,t)=>(o(),f(`article`,{key:String(e.id??t),class:`evidence-card`},[u(`strong`,null,p(e.name||e.source_description||`来源记录`),1),u(`p`,null,p(e.summary||e.source_description),1),u(`span`,null,p(e.source_kind||`来源`)+` · `+p(n(I)(e.reference_time||e.created_at)),1),e.source_excerpt?(o(),f(`p`,at,p(e.source_excerpt),1)):m(``,!0)]))),128))])],64)):m(``,!0),e.item.revisions.length?(o(),f(l,{key:10},[i[41]||=u(`h4`,{class:`inspector-subtitle`},`修订历史`,-1),u(`div`,ot,[(o(!0),f(l,null,t(e.item.revisions,(e,t)=>(o(),f(`article`,{key:String(e.id??t),class:`history-row`},[u(`strong`,null,p(n(D)(e.action)),1),u(`p`,null,p(e.reason??e.summary??`未填写说明`),1),u(`span`,null,p(n(I)(e.created_at)),1)]))),128))])],64)):m(``,!0),e.item.auditEvents.length?(o(),f(l,{key:11},[i[42]||=u(`h4`,{class:`inspector-subtitle`},`人工与 Agent 记录`,-1),u(`div`,st,[(o(!0),f(l,null,t(e.item.auditEvents,e=>(o(),f(`article`,{key:e.id,class:`history-row`},[u(`strong`,null,p(n(M)(e.action)),1),u(`p`,null,p(e.reason),1),u(`span`,null,` 版本 `+p(e.human_change_version)+` · `+p(n(I)(e.created_at)),1),e.action===`agent_review`?(o(),f(`small`,ct,` 冲突:`+p(e.conflict_check===`no_conflict`?`无冲突`:`有冲突`)+` · 分类:`+p(e.classification_check===`reasonable`?`合理`:`需调整`),1)):m(``,!0)]))),128))])],64)):m(``,!0)],64)):(o(),f(l,{key:1},[i[43]||=u(`p`,{class:`eyebrow`},`内容详情`,-1),i[44]||=u(`h3`,null,`选择一个节点或关系`,-1),i[45]||=u(`p`,{class:`inspector-placeholder`},`点击后查看完整说明、来源证据和关联关系。`,-1)],64))]))}});export{H as i,ke as n,ne as r,lt as t};
@@ -1 +0,0 @@
1
- .knowledge-confirm-dialog[data-v-65eb248e]{width:min(680px,100vw - 48px)}.knowledge-confirm-dialog-shell[data-v-65eb248e]{gap:18px;padding:24px;display:grid}.knowledge-confirm-summary[data-v-65eb248e]{border-top:1px solid #e1e5e2;border-bottom:1px solid #e1e5e2;gap:6px;padding:14px 0;display:grid}.knowledge-confirm-summary span[data-v-65eb248e],.knowledge-confirm-summary small[data-v-65eb248e]{color:#7d8780;font-size:10px}.knowledge-confirm-summary strong[data-v-65eb248e]{color:#29382f;font-size:16px}.knowledge-confirm-summary p[data-v-65eb248e]{color:#606b63;font-size:12px;line-height:1.65}.knowledge-confirm-fields[data-v-65eb248e]{gap:14px;display:grid}.knowledge-confirm-fields label[data-v-65eb248e]{color:#59645c;gap:6px;font-size:11px;font-weight:600;display:grid}.knowledge-confirm-acknowledgement[data-v-65eb248e]{color:#4e5c53;grid-template-columns:auto minmax(0,1fr);align-items:start;gap:9px;font-size:11px;line-height:1.5;display:grid}
@@ -1 +0,0 @@
1
- .batch-confirm-dialog[data-v-f18528e4]{width:min(980px,100vw - 64px)}.batch-confirm-form[data-v-f18528e4]{grid-template-columns:320px minmax(0,1fr);gap:24px;padding-top:20px;display:grid}.batch-confirm-controls[data-v-f18528e4]{align-content:start;gap:14px;display:grid}.batch-confirm-controls>label[data-v-f18528e4],.batch-confirmer-fields label[data-v-f18528e4]{color:#5f6962;gap:6px;font-size:10px;font-weight:600;display:grid}.batch-confirm-controls[data-v-f18528e4] .searchable-select{width:100%}.batch-group-summary[data-v-f18528e4]{border-top:1px solid #e1e5e2;border-bottom:1px solid #e1e5e2;gap:4px;padding:12px 0;display:grid}.batch-group-summary span[data-v-f18528e4],.batch-group-summary small[data-v-f18528e4]{color:#89918b;font-size:8px}.batch-group-summary strong[data-v-f18528e4]{color:#35443b;font-size:12px}.batch-group-summary p[data-v-f18528e4]{color:#6e7871;font-size:9px;line-height:1.5}.batch-confirmer-fields[data-v-f18528e4]{grid-template-columns:112px minmax(0,1fr);gap:8px;display:grid}.batch-confirm-rule[data-v-f18528e4],.batch-limit-note[data-v-f18528e4]{color:#7c857f;font-size:9px;line-height:1.5}.batch-confirm-review[data-v-f18528e4]{border-left:1px solid #e1e5e2;min-width:0;padding-left:24px}.batch-review-heading[data-v-f18528e4]{justify-content:space-between;align-items:center;gap:16px;padding-bottom:10px;display:flex}.batch-review-heading h4[data-v-f18528e4]{color:#344239;font-size:13px}.batch-review-heading p[data-v-f18528e4]{color:#858e87;margin-top:2px;font-size:9px}.batch-review-list[data-v-f18528e4]{border-top:1px solid #e3e6e3;border-bottom:1px solid #e3e6e3;max-height:410px;overflow:auto}.batch-review-item[data-v-f18528e4]{cursor:pointer;border-bottom:1px solid #eceeec;grid-template-columns:auto minmax(0,1fr);gap:9px;padding:10px 2px;display:grid}.batch-review-item[data-v-f18528e4]:last-child{border-bottom:0}.batch-review-item>input[data-v-f18528e4]{margin-top:2px}.batch-review-item>span[data-v-f18528e4]{gap:3px;min-width:0;display:grid}.batch-review-item strong[data-v-f18528e4]{color:#3a4740;font-size:10px}.batch-review-item small[data-v-f18528e4]{color:#77817a;font-size:8px}.batch-review-item em[data-v-f18528e4]{color:#848d87;text-overflow:ellipsis;white-space:nowrap;font-size:8px;font-style:normal;line-height:1.4;overflow:hidden}.batch-confirm-acknowledgement[data-v-f18528e4]{color:#4f5d55;cursor:pointer;align-items:flex-start;gap:8px;margin-top:12px;font-size:9px;line-height:1.5;display:flex}.batch-confirm-form>.publish-dialog-error[data-v-f18528e4],.batch-confirm-form>.publish-dialog-actions[data-v-f18528e4]{grid-column:1/-1}@media (width<=820px){.batch-confirm-form[data-v-f18528e4]{grid-template-columns:1fr}.batch-confirm-review[data-v-f18528e4]{border-left:0;padding:0}}.knowledge-organizer[data-v-90794584]{flex-direction:column;gap:12px;padding:18px 24px 20px;display:flex;overflow:hidden}.organizer-head[data-v-90794584]{grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:12px 24px;min-width:0;display:grid}.organizer-principle[data-v-90794584]{align-self:center;min-width:0;padding:0 2px}.organizer-principle h3[data-v-90794584]{font-size:16px}.organizer-principle>p[data-v-90794584]:last-child{color:#6e7771;max-width:760px;margin-top:5px;font-size:11px;line-height:1.55}.organizer-attention[data-v-90794584]{text-align:right;border-left:1px solid #dde2de;grid-area:1/2;min-width:152px;padding-left:20px}.organizer-attention strong[data-v-90794584],.organizer-attention span[data-v-90794584],.organizer-attention small[data-v-90794584]{display:block}.organizer-attention strong[data-v-90794584]{color:#6f582b;font-size:23px;font-weight:620}.organizer-attention span[data-v-90794584]{color:#5f675f;font-size:10px;font-weight:650}.organizer-attention small[data-v-90794584]{color:#8a918b;margin-top:2px;font-size:8px}.organizer-loading-inline[data-v-90794584]{color:#7b847e;text-align:center;font-size:10px}.organizer-toolbar[data-v-90794584]{align-items:center;gap:8px;min-height:34px;display:flex}.organizer-search[data-v-90794584]{flex:300px;min-width:180px;max-width:360px}.organizer-search>span[data-v-90794584]{clip:rect(0 0 0 0);width:1px;height:1px;position:absolute;overflow:hidden}.organizer-search input[data-v-90794584]{width:100%}.review-state-filter[data-v-90794584],.quadrant-filter[data-v-90794584]{white-space:nowrap;background:#f1f4f1;border:1px solid #d7ddd8;border-radius:7px;flex:none;align-items:center;gap:2px;height:31px;padding:2px;display:flex}.quadrant-filter[data-v-90794584]{max-width:470px;overflow-x:auto}.review-state-filter>span[data-v-90794584],.quadrant-filter>span[data-v-90794584]{color:#7a847d;padding:0 6px;font-size:8px;font-weight:650}.review-state-filter button[data-v-90794584],.quadrant-filter button[data-v-90794584]{color:#626d66;white-space:nowrap;background:0 0;border:0;border-radius:5px;height:25px;padding:0 7px;font-size:9px}.review-state-filter button[data-v-90794584]:hover,.quadrant-filter button[data-v-90794584]:hover{color:#35473d;background:#ffffffa8}.review-state-filter button.active[data-v-90794584],.quadrant-filter button.active[data-v-90794584]{color:#30483a;background:#fff;box-shadow:0 1px 3px #2f3d341f}.review-state-filter button.state-pending.active[data-v-90794584]{color:#765d2e}.review-state-filter button.state-agent_confirmed.active[data-v-90794584]{color:#4d5f78}.review-state-filter button.state-confirmed.active[data-v-90794584]{color:#356047}.review-state-filter button strong[data-v-90794584],.quadrant-filter button strong[data-v-90794584]{margin-left:2px;font-size:8px;font-weight:720}.organizer-result-summary[data-v-90794584]{color:#7b847e;white-space:nowrap;flex:none;font-size:9px}.organizer-toolbar .batch-confirm-action[data-v-90794584]{margin-left:auto}.organizer-toolbar .toolbar-action+.toolbar-action[data-v-90794584]{margin-left:0}.organizer-truncated[data-v-90794584]{color:#806536;margin:-4px 0 0;font-size:9px}.organizer-layout[data-v-90794584]{background:#fff;border:1px solid #cfd7d1;border-radius:8px;flex:1;grid-template-columns:minmax(0,1fr);min-height:0;display:grid;overflow:hidden}.organizer-layout.has-selection[data-v-90794584]{grid-template-columns:minmax(0,1fr) 340px}.organizer-layout[data-v-90794584]:not(.has-selection) .graph-inspector{display:none}.organizer-directory[data-v-90794584]{overscroll-behavior:contain;min-width:0;overflow:auto}.organizer-table-head[data-v-90794584],.organizer-row[data-v-90794584]{grid-template-columns:minmax(230px,1.35fr) 114px 130px minmax(160px,.8fr) 118px;align-items:center;gap:12px;min-width:920px;display:grid}.organizer-table-head[data-v-90794584]{z-index:2;color:#8b938d;background:#f7f8f6;border-bottom:1px solid #e0e4e0;padding:10px 15px;font-size:9px;font-weight:680;position:sticky;top:0}.organizer-row[data-v-90794584]{color:#4f5952;text-align:left;background:#fff;border:0;border-bottom:1px solid #e8ebe8;width:100%;min-height:68px;padding:11px 15px}.organizer-row[data-v-90794584]:hover{background:#f8faf8}.organizer-row.selected[data-v-90794584]{background:#f1f6f3;box-shadow:inset 2px 0 #557863}.organizer-item-copy[data-v-90794584]{gap:4px;min-width:0;display:grid}.organizer-item-copy strong[data-v-90794584],.organizer-item-copy small[data-v-90794584]{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.organizer-item-copy strong[data-v-90794584]{color:#2f3933;font-size:11px;font-weight:630}.organizer-item-copy small[data-v-90794584],.organizer-basis[data-v-90794584],.organizer-row time[data-v-90794584]{color:#7b847e;font-size:9px}.quadrant-chip[data-v-90794584],.review-chip[data-v-90794584]{white-space:nowrap;border-radius:999px;width:fit-content;padding:4px 7px;font-size:9px;font-weight:650}.quadrant-chip[data-v-90794584]{color:#59655e;background:#f0f2ef;gap:1px;display:grid}.quadrant-chip small[data-v-90794584]{color:inherit;opacity:.75;font-size:7px;font-weight:520}.quadrant-chip.known_known[data-v-90794584]{color:#356047;background:#edf6f0}.quadrant-chip.known_unknown[data-v-90794584]{color:#77602f;background:#f7f2e5}.quadrant-chip.unknown_known[data-v-90794584]{color:#4d5f78;background:#eef2f7}.quadrant-chip.unknown_unknown[data-v-90794584]{color:#725d70;background:#f5eff4}.review-chip.state-confirmed[data-v-90794584]{color:#356047;background:#edf6f0}.review-chip.state-agent_confirmed[data-v-90794584]{color:#4d5f78;background:#eef2f7}.review-chip.state-pending[data-v-90794584]{color:#77602f;background:#f7f2e5}.organizer-basis[data-v-90794584]{overflow-wrap:anywhere}@media (width<=1260px){.review-state-filter>span[data-v-90794584],.quadrant-filter>span[data-v-90794584],.organizer-result-summary[data-v-90794584]{display:none}.organizer-table-head[data-v-90794584],.organizer-row[data-v-90794584]{grid-template-columns:minmax(210px,1fr) 108px 104px minmax(140px,.8fr) 104px;min-width:760px}}@media (width<=1040px){.organizer-layout.has-selection[data-v-90794584]{grid-template-columns:minmax(0,1fr) 280px}.organizer-table-head[data-v-90794584],.organizer-row[data-v-90794584]{grid-template-columns:minmax(210px,1fr) 108px 104px;min-width:470px}.organizer-table-head[data-v-90794584]>:nth-child(n+4),.organizer-basis[data-v-90794584],.organizer-row time[data-v-90794584]{display:none}.batch-confirm-action[data-v-90794584]{text-overflow:ellipsis;white-space:nowrap;max-width:108px;overflow:hidden}}