@pellux/goodvibes-sdk 0.26.11 → 0.27.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.
- package/dist/_internal/contracts/artifacts/operator-contract.json +3110 -4
- package/dist/_internal/contracts/generated/foundation-metadata.d.ts +2 -2
- package/dist/_internal/contracts/generated/foundation-metadata.js +2 -2
- package/dist/_internal/contracts/generated/operator-contract.d.ts.map +1 -1
- package/dist/_internal/contracts/generated/operator-contract.js +3110 -4
- package/dist/_internal/contracts/generated/operator-method-ids.d.ts +1 -1
- package/dist/_internal/contracts/generated/operator-method-ids.d.ts.map +1 -1
- package/dist/_internal/contracts/generated/operator-method-ids.js +8 -0
- package/dist/_internal/platform/control-plane/method-catalog-knowledge.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/method-catalog-knowledge.js +101 -0
- package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts +1 -0
- package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.js +1 -1
- package/dist/_internal/platform/control-plane/operator-contract-schemas-project-planning.d.ts +8 -0
- package/dist/_internal/platform/control-plane/operator-contract-schemas-project-planning.d.ts.map +1 -0
- package/dist/_internal/platform/control-plane/operator-contract-schemas-project-planning.js +135 -0
- package/dist/_internal/platform/daemon/facade-composition.d.ts.map +1 -1
- package/dist/_internal/platform/daemon/facade-composition.js +2 -0
- package/dist/_internal/platform/daemon/facade-types.d.ts +2 -1
- package/dist/_internal/platform/daemon/facade-types.d.ts.map +1 -1
- package/dist/_internal/platform/daemon/http/project-planning-routes.d.ts +18 -0
- package/dist/_internal/platform/daemon/http/project-planning-routes.d.ts.map +1 -0
- package/dist/_internal/platform/daemon/http/project-planning-routes.js +68 -0
- package/dist/_internal/platform/daemon/http/router.d.ts +4 -1
- package/dist/_internal/platform/daemon/http/router.d.ts.map +1 -1
- package/dist/_internal/platform/daemon/http/router.js +18 -0
- package/dist/_internal/platform/daemon/types.d.ts +2 -1
- package/dist/_internal/platform/daemon/types.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/helpers.d.ts +2 -0
- package/dist/_internal/platform/knowledge/home-graph/helpers.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/helpers.js +17 -2
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/search.js +93 -9
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/service.js +10 -8
- package/dist/_internal/platform/knowledge/home-graph/space-selection.d.ts +7 -0
- package/dist/_internal/platform/knowledge/home-graph/space-selection.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/space-selection.js +36 -0
- package/dist/_internal/platform/knowledge/index.d.ts +3 -1
- package/dist/_internal/platform/knowledge/index.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/index.js +2 -1
- package/dist/_internal/platform/knowledge/project-planning/helpers.d.ts +17 -0
- package/dist/_internal/platform/knowledge/project-planning/helpers.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/project-planning/helpers.js +65 -0
- package/dist/_internal/platform/knowledge/project-planning/index.d.ts +6 -0
- package/dist/_internal/platform/knowledge/project-planning/index.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/project-planning/index.js +3 -0
- package/dist/_internal/platform/knowledge/project-planning/readiness.d.ts +3 -0
- package/dist/_internal/platform/knowledge/project-planning/readiness.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/project-planning/readiness.js +124 -0
- package/dist/_internal/platform/knowledge/project-planning/service.d.ts +25 -0
- package/dist/_internal/platform/knowledge/project-planning/service.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/project-planning/service.js +259 -0
- package/dist/_internal/platform/knowledge/project-planning/types.d.ts +203 -0
- package/dist/_internal/platform/knowledge/project-planning/types.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/project-planning/types.js +1 -0
- package/dist/_internal/platform/knowledge/spaces.d.ts +4 -0
- package/dist/_internal/platform/knowledge/spaces.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/spaces.js +10 -0
- package/dist/_internal/platform/runtime/services.d.ts +2 -1
- package/dist/_internal/platform/runtime/services.d.ts.map +1 -1
- package/dist/_internal/platform/runtime/services.js +5 -1
- package/dist/_internal/platform/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { basename, resolve } from 'node:path';
|
|
3
|
+
import { PROJECT_KNOWLEDGE_SPACE_PREFIX, isProjectKnowledgeSpace, normalizeKnowledgeSpaceId, normalizeProjectId, normalizeSpaceComponent, projectKnowledgeSpaceId, } from '../spaces.js';
|
|
4
|
+
export const PROJECT_PLANNING_CONNECTOR_ID = 'goodvibes-project-planning';
|
|
5
|
+
export const PROJECT_PLANNING_TAG = 'project-planning';
|
|
6
|
+
export function projectPlanningProjectIdFromPath(path) {
|
|
7
|
+
const resolved = resolve(path);
|
|
8
|
+
const name = normalizeSpaceComponent(basename(resolved) || 'project');
|
|
9
|
+
const digest = createHash('sha256').update(resolved).digest('hex').slice(0, 10);
|
|
10
|
+
return `${name}-${digest}`;
|
|
11
|
+
}
|
|
12
|
+
export function resolveProjectPlanningSpace(input = {}, defaultProjectId = 'default') {
|
|
13
|
+
const explicitSpace = typeof input.knowledgeSpaceId === 'string'
|
|
14
|
+
? normalizeKnowledgeSpaceId(input.knowledgeSpaceId)
|
|
15
|
+
: '';
|
|
16
|
+
if (explicitSpace && isProjectKnowledgeSpace(explicitSpace)) {
|
|
17
|
+
return {
|
|
18
|
+
knowledgeSpaceId: explicitSpace,
|
|
19
|
+
projectId: projectIdFromSpace(explicitSpace),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const projectId = normalizeProjectId(input.projectId ?? defaultProjectId);
|
|
23
|
+
return {
|
|
24
|
+
projectId,
|
|
25
|
+
knowledgeSpaceId: projectKnowledgeSpaceId(projectId),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function projectIdFromSpace(spaceId) {
|
|
29
|
+
const normalized = normalizeKnowledgeSpaceId(spaceId);
|
|
30
|
+
if (!normalized.startsWith(PROJECT_KNOWLEDGE_SPACE_PREFIX)) {
|
|
31
|
+
return normalizeProjectId(normalized);
|
|
32
|
+
}
|
|
33
|
+
return normalizeProjectId(normalized.slice(PROJECT_KNOWLEDGE_SPACE_PREFIX.length));
|
|
34
|
+
}
|
|
35
|
+
export function projectPlanningCanonicalUri(spaceId, kind, id) {
|
|
36
|
+
return `goodvibes://planning/${encodeURIComponent(spaceId)}/${kind}/${encodeURIComponent(id)}`;
|
|
37
|
+
}
|
|
38
|
+
export function projectPlanningSourceId(spaceId, kind, id) {
|
|
39
|
+
const digest = createHash('sha256').update(`${spaceId}:${kind}:${id}`).digest('hex').slice(0, 16);
|
|
40
|
+
return `project-planning-${kind}-${digest}`;
|
|
41
|
+
}
|
|
42
|
+
export function projectPlanningArtifactSummary(kind, value) {
|
|
43
|
+
if (kind === 'state') {
|
|
44
|
+
const state = value;
|
|
45
|
+
return state.goal ? `Planning state for ${state.goal}` : 'Project planning state.';
|
|
46
|
+
}
|
|
47
|
+
if (kind === 'decision') {
|
|
48
|
+
const decision = value;
|
|
49
|
+
return decision.decision ? `${decision.title ?? 'Decision'}: ${decision.decision}` : 'Project planning decision record.';
|
|
50
|
+
}
|
|
51
|
+
const language = value;
|
|
52
|
+
return `Project language artifact with ${language.terms?.length ?? 0} terms and ${language.ambiguities?.length ?? 0} resolved ambiguities.`;
|
|
53
|
+
}
|
|
54
|
+
export function stablePlanningId(prefix, value) {
|
|
55
|
+
const normalized = normalizeSpaceComponent(value);
|
|
56
|
+
if (normalized !== 'default')
|
|
57
|
+
return normalized;
|
|
58
|
+
const digest = createHash('sha256').update(value || prefix).digest('hex').slice(0, 10);
|
|
59
|
+
return `${prefix}-${digest}`;
|
|
60
|
+
}
|
|
61
|
+
export function readPlanningMetadataObject(value) {
|
|
62
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
63
|
+
? value
|
|
64
|
+
: {};
|
|
65
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ProjectPlanningService } from './service.js';
|
|
2
|
+
export type { ProjectPlanningServiceOptions } from './service.js';
|
|
3
|
+
export { PROJECT_PLANNING_CONNECTOR_ID, PROJECT_PLANNING_TAG, projectPlanningCanonicalUri, projectPlanningProjectIdFromPath, projectPlanningSourceId, resolveProjectPlanningSpace, } from './helpers.js';
|
|
4
|
+
export { evaluateProjectPlanningReadiness } from './readiness.js';
|
|
5
|
+
export type { ProjectPlanningAgentAssignment, ProjectPlanningAmbiguity, ProjectPlanningDecision, ProjectPlanningDecisionRecordInput, ProjectPlanningDecisionResult, ProjectPlanningDecisionsResult, ProjectPlanningDecisionStatus, ProjectPlanningDependency, ProjectPlanningEvaluateInput, ProjectPlanningEvaluation, ProjectPlanningGap, ProjectPlanningGapKind, ProjectPlanningGapSeverity, ProjectPlanningGateStatus, ProjectPlanningLanguageArtifact, ProjectPlanningLanguageResult, ProjectPlanningLanguageUpsertInput, ProjectPlanningQuestion, ProjectPlanningQuestionStatus, ProjectPlanningReadiness, ProjectPlanningSpaceInput, ProjectPlanningState, ProjectPlanningStateResult, ProjectPlanningStateUpsertInput, ProjectPlanningStatus, ProjectPlanningTask, ProjectPlanningTaskStatus, ProjectPlanningTerm, ProjectPlanningVerificationGate, } from './types.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/project-planning/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,YAAY,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,2BAA2B,EAC3B,gCAAgC,EAChC,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gCAAgC,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EACV,8BAA8B,EAC9B,wBAAwB,EACxB,uBAAuB,EACvB,kCAAkC,EAClC,6BAA6B,EAC7B,8BAA8B,EAC9B,6BAA6B,EAC7B,yBAAyB,EACzB,4BAA4B,EAC5B,yBAAyB,EACzB,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EACzB,+BAA+B,EAC/B,6BAA6B,EAC7B,kCAAkC,EAClC,uBAAuB,EACvB,6BAA6B,EAC7B,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,EAC1B,+BAA+B,EAC/B,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,+BAA+B,GAChC,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { ProjectPlanningService } from './service.js';
|
|
2
|
+
export { PROJECT_PLANNING_CONNECTOR_ID, PROJECT_PLANNING_TAG, projectPlanningCanonicalUri, projectPlanningProjectIdFromPath, projectPlanningSourceId, resolveProjectPlanningSpace, } from './helpers.js';
|
|
3
|
+
export { evaluateProjectPlanningReadiness } from './readiness.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/project-planning/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EAIzB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAiBpB,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,oBAAoB,GAAG,yBAAyB,CA2GvG"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const VAGUE_TERMS = [
|
|
2
|
+
'better',
|
|
3
|
+
'improve',
|
|
4
|
+
'improved',
|
|
5
|
+
'setup',
|
|
6
|
+
'integration',
|
|
7
|
+
'agent channel',
|
|
8
|
+
'remote',
|
|
9
|
+
'thing',
|
|
10
|
+
'stuff',
|
|
11
|
+
'etc',
|
|
12
|
+
'clean up',
|
|
13
|
+
'fix it',
|
|
14
|
+
];
|
|
15
|
+
export function evaluateProjectPlanningReadiness(state) {
|
|
16
|
+
const gaps = [];
|
|
17
|
+
const goal = state.goal.trim();
|
|
18
|
+
if (!goal) {
|
|
19
|
+
gaps.push(blockingQuestion('missing-goal', 'The plan needs a concrete goal before it can be executed.', 'What outcome should this plan produce?', 'A clear outcome lets the TUI inspect the right code and ask only relevant follow-up questions.', 'State the user-visible behavior or project change that should exist when the work is done.'));
|
|
20
|
+
}
|
|
21
|
+
if (!state.scope?.trim() && state.constraints.length === 0) {
|
|
22
|
+
gaps.push(blockingQuestion('missing-scope', 'The plan has no explicit boundary for what is included or excluded.', 'What is in scope, and what should be left out for this pass?', 'Scope boundaries prevent the planning loop from turning a focused change into unrelated work.', 'Define the first-pass scope and record do-later items separately.'));
|
|
23
|
+
}
|
|
24
|
+
for (const question of state.openQuestions) {
|
|
25
|
+
if ((question.status ?? 'open') === 'open') {
|
|
26
|
+
gaps.push({
|
|
27
|
+
id: `open-question:${question.id}`,
|
|
28
|
+
kind: 'open-question',
|
|
29
|
+
severity: 'blocking',
|
|
30
|
+
message: `Open planning question: ${question.prompt}`,
|
|
31
|
+
question,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const vagueTerm = firstVagueTerm(goal);
|
|
36
|
+
if (vagueTerm && state.answeredQuestions.length === 0 && state.decisions.length === 0) {
|
|
37
|
+
gaps.push(blockingQuestion('ambiguous-language', `The goal uses ambiguous language (${JSON.stringify(vagueTerm)}) without recorded clarification.`, `When you say ${JSON.stringify(vagueTerm)}, what concrete behavior should change?`, 'GoodVibes should challenge vague words before work starts so future agents do not implement the wrong thing.', 'Define the term in project language or replace it with concrete expected behavior.'));
|
|
38
|
+
}
|
|
39
|
+
if (goal && state.tasks.length === 0) {
|
|
40
|
+
gaps.push(blockingQuestion('missing-tasks', 'The plan has no decomposed tasks.', 'What are the smallest useful implementation tasks for this goal?', 'Task decomposition is what lets the TUI identify dependencies, parallel agent work, and verification gates.', 'Create task records with likely files, dependencies, and verification notes.'));
|
|
41
|
+
}
|
|
42
|
+
if (state.tasks.length > 1 && state.dependencies.length === 0) {
|
|
43
|
+
gaps.push({
|
|
44
|
+
id: 'missing-dependencies',
|
|
45
|
+
kind: 'missing-dependencies',
|
|
46
|
+
severity: 'advisory',
|
|
47
|
+
message: 'Multiple tasks exist but no dependency graph has been recorded.',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const tasksWithoutVerification = state.tasks
|
|
51
|
+
.filter((task) => (task.verification?.length ?? 0) === 0)
|
|
52
|
+
.map((task) => task.id);
|
|
53
|
+
const hasRequiredGate = state.verificationGates.some((gate) => gate.required !== false);
|
|
54
|
+
if (state.tasks.length > 0 && tasksWithoutVerification.length > 0 && !hasRequiredGate) {
|
|
55
|
+
gaps.push({
|
|
56
|
+
id: 'missing-verification',
|
|
57
|
+
kind: 'missing-verification',
|
|
58
|
+
severity: 'blocking',
|
|
59
|
+
message: 'The plan has tasks but no verification gates or per-task verification.',
|
|
60
|
+
question: {
|
|
61
|
+
id: 'verification-gates',
|
|
62
|
+
prompt: 'How should this plan prove that the work is correct?',
|
|
63
|
+
whyItMatters: 'Verification gates keep execution from ending at code changes that were never checked.',
|
|
64
|
+
recommendedAnswer: 'Record concrete tests, commands, manual checks, or release gates for the changed behavior.',
|
|
65
|
+
consequence: 'The plan should not be executable until verification exists.',
|
|
66
|
+
},
|
|
67
|
+
relatedTaskIds: tasksWithoutVerification,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const blocking = gaps.some((gap) => gap.severity === 'blocking');
|
|
71
|
+
if (!blocking && !state.executionApproved) {
|
|
72
|
+
gaps.push({
|
|
73
|
+
id: 'unapproved-execution',
|
|
74
|
+
kind: 'unapproved-execution',
|
|
75
|
+
severity: 'blocking',
|
|
76
|
+
message: 'The plan is structurally ready but has not been approved for execution.',
|
|
77
|
+
question: {
|
|
78
|
+
id: 'approve-execution',
|
|
79
|
+
prompt: 'Is this plan approved for execution?',
|
|
80
|
+
whyItMatters: 'The TUI owns user approval before local work or agent assignments begin.',
|
|
81
|
+
recommendedAnswer: 'Approve only after the goal, scope, tasks, dependencies, and verification gates look right.',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const readiness = readinessFromGaps(gaps);
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
projectId: state.projectId,
|
|
89
|
+
knowledgeSpaceId: state.knowledgeSpaceId,
|
|
90
|
+
readiness,
|
|
91
|
+
gaps,
|
|
92
|
+
...(gaps[0]?.question ? { nextQuestion: gaps[0].question } : {}),
|
|
93
|
+
state: {
|
|
94
|
+
...state,
|
|
95
|
+
readiness,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function readinessFromGaps(gaps) {
|
|
100
|
+
if (gaps.length === 0)
|
|
101
|
+
return 'executable';
|
|
102
|
+
if (gaps.some((gap) => gap.severity === 'blocking'))
|
|
103
|
+
return 'needs-user-input';
|
|
104
|
+
return 'not-ready';
|
|
105
|
+
}
|
|
106
|
+
function firstVagueTerm(value) {
|
|
107
|
+
const normalized = value.toLowerCase();
|
|
108
|
+
return VAGUE_TERMS.find((term) => normalized.includes(term)) ?? null;
|
|
109
|
+
}
|
|
110
|
+
function blockingQuestion(kind, message, prompt, whyItMatters, recommendedAnswer) {
|
|
111
|
+
const question = {
|
|
112
|
+
id: kind,
|
|
113
|
+
prompt,
|
|
114
|
+
whyItMatters,
|
|
115
|
+
recommendedAnswer,
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
id: kind,
|
|
119
|
+
kind,
|
|
120
|
+
severity: 'blocking',
|
|
121
|
+
message,
|
|
122
|
+
question,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { KnowledgeStore } from '../store.js';
|
|
2
|
+
import type { ProjectPlanningDecisionRecordInput, ProjectPlanningDecisionResult, ProjectPlanningDecisionsResult, ProjectPlanningEvaluateInput, ProjectPlanningEvaluation, ProjectPlanningLanguageResult, ProjectPlanningLanguageUpsertInput, ProjectPlanningSpaceInput, ProjectPlanningStateResult, ProjectPlanningStateUpsertInput, ProjectPlanningStatus } from './types.js';
|
|
3
|
+
export interface ProjectPlanningServiceOptions {
|
|
4
|
+
readonly defaultProjectId?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ProjectPlanningService {
|
|
7
|
+
private readonly store;
|
|
8
|
+
private readonly defaultProjectId;
|
|
9
|
+
constructor(store: KnowledgeStore, options?: ProjectPlanningServiceOptions);
|
|
10
|
+
status(input?: ProjectPlanningSpaceInput): Promise<ProjectPlanningStatus>;
|
|
11
|
+
getState(input?: ProjectPlanningSpaceInput & {
|
|
12
|
+
readonly planningId?: string;
|
|
13
|
+
}): Promise<ProjectPlanningStateResult>;
|
|
14
|
+
upsertState(input: ProjectPlanningStateUpsertInput): Promise<ProjectPlanningStateResult>;
|
|
15
|
+
evaluate(input?: ProjectPlanningEvaluateInput): Promise<ProjectPlanningEvaluation>;
|
|
16
|
+
listDecisions(input?: ProjectPlanningSpaceInput): Promise<ProjectPlanningDecisionsResult>;
|
|
17
|
+
recordDecision(input: ProjectPlanningDecisionRecordInput): Promise<ProjectPlanningDecisionResult>;
|
|
18
|
+
getLanguage(input?: ProjectPlanningSpaceInput): Promise<ProjectPlanningLanguageResult>;
|
|
19
|
+
upsertLanguage(input: ProjectPlanningLanguageUpsertInput): Promise<ProjectPlanningLanguageResult>;
|
|
20
|
+
private resolveSpace;
|
|
21
|
+
private sourcesForSpace;
|
|
22
|
+
private getArtifactSource;
|
|
23
|
+
private upsertArtifactSource;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/project-planning/service.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAa7C,OAAO,KAAK,EAEV,kCAAkC,EAClC,6BAA6B,EAC7B,8BAA8B,EAC9B,4BAA4B,EAC5B,yBAAyB,EAEzB,6BAA6B,EAC7B,kCAAkC,EAClC,yBAAyB,EAEzB,0BAA0B,EAC1B,+BAA+B,EAC/B,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAHxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAGvB,KAAK,EAAE,cAAc,EACtC,OAAO,GAAE,6BAAkC;IAKvC,MAAM,CAAC,KAAK,GAAE,yBAA8B,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAyB7E,QAAQ,CAAC,KAAK,GAAE,yBAAyB,GAAG;QAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAevH,WAAW,CAAC,KAAK,EAAE,+BAA+B,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAgBxF,QAAQ,CAAC,KAAK,GAAE,4BAAiC,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAWtF,aAAa,CAAC,KAAK,GAAE,yBAA8B,GAAG,OAAO,CAAC,8BAA8B,CAAC;IAgB7F,cAAc,CAAC,KAAK,EAAE,kCAAkC,GAAG,OAAO,CAAC,6BAA6B,CAAC;IA2BjG,WAAW,CAAC,KAAK,GAAE,yBAA8B,GAAG,OAAO,CAAC,6BAA6B,CAAC;IAc1F,cAAc,CAAC,KAAK,EAAE,kCAAkC,GAAG,OAAO,CAAC,6BAA6B,CAAC;IA2BvG,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,iBAAiB;YASX,oBAAoB;CAyBnC"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { knowledgeSpaceMetadata, normalizeProjectId, } from '../spaces.js';
|
|
2
|
+
import { KnowledgeStore } from '../store.js';
|
|
3
|
+
import { PROJECT_PLANNING_CONNECTOR_ID, PROJECT_PLANNING_TAG, projectPlanningArtifactSummary, projectPlanningCanonicalUri, projectPlanningSourceId, readPlanningMetadataObject, resolveProjectPlanningSpace, stablePlanningId, } from './helpers.js';
|
|
4
|
+
import { evaluateProjectPlanningReadiness } from './readiness.js';
|
|
5
|
+
export class ProjectPlanningService {
|
|
6
|
+
store;
|
|
7
|
+
defaultProjectId;
|
|
8
|
+
constructor(store, options = {}) {
|
|
9
|
+
this.store = store;
|
|
10
|
+
this.defaultProjectId = normalizeProjectId(options.defaultProjectId ?? 'default');
|
|
11
|
+
}
|
|
12
|
+
async status(input = {}) {
|
|
13
|
+
await this.store.init();
|
|
14
|
+
const space = this.resolveSpace(input);
|
|
15
|
+
const sources = this.sourcesForSpace(space.knowledgeSpaceId);
|
|
16
|
+
return {
|
|
17
|
+
ok: true,
|
|
18
|
+
projectId: space.projectId,
|
|
19
|
+
knowledgeSpaceId: space.knowledgeSpaceId,
|
|
20
|
+
passiveOnly: true,
|
|
21
|
+
counts: {
|
|
22
|
+
states: sources.filter((source) => artifactKind(source) === 'state').length,
|
|
23
|
+
decisions: sources.filter((source) => artifactKind(source) === 'decision').length,
|
|
24
|
+
languageArtifacts: sources.filter((source) => artifactKind(source) === 'language').length,
|
|
25
|
+
},
|
|
26
|
+
capabilities: [
|
|
27
|
+
'project-scoped-storage',
|
|
28
|
+
'planning-state-validation',
|
|
29
|
+
'project-language-artifacts',
|
|
30
|
+
'decision-records',
|
|
31
|
+
'agent-task-graph-metadata',
|
|
32
|
+
'passive-daemon-only',
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async getState(input = {}) {
|
|
37
|
+
await this.store.init();
|
|
38
|
+
const space = this.resolveSpace(input);
|
|
39
|
+
const planningId = normalizePlanningId(input.planningId);
|
|
40
|
+
const source = this.getArtifactSource(space.knowledgeSpaceId, 'state', planningId);
|
|
41
|
+
const state = source ? readState(source) : null;
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
projectId: space.projectId,
|
|
45
|
+
knowledgeSpaceId: space.knowledgeSpaceId,
|
|
46
|
+
state,
|
|
47
|
+
...(source ? { source } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async upsertState(input) {
|
|
51
|
+
await this.store.init();
|
|
52
|
+
const space = this.resolveSpace(input);
|
|
53
|
+
const state = normalizeState(input.state, space.projectId, space.knowledgeSpaceId);
|
|
54
|
+
const evaluation = evaluateProjectPlanningReadiness(state);
|
|
55
|
+
const normalized = evaluation.state;
|
|
56
|
+
const source = await this.upsertArtifactSource(space, 'state', normalized.id, normalized);
|
|
57
|
+
return {
|
|
58
|
+
ok: true,
|
|
59
|
+
projectId: space.projectId,
|
|
60
|
+
knowledgeSpaceId: space.knowledgeSpaceId,
|
|
61
|
+
state: normalized,
|
|
62
|
+
source,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async evaluate(input = {}) {
|
|
66
|
+
await this.store.init();
|
|
67
|
+
const space = this.resolveSpace(input);
|
|
68
|
+
if (input.state) {
|
|
69
|
+
return evaluateProjectPlanningReadiness(normalizeState(input.state, space.projectId, space.knowledgeSpaceId));
|
|
70
|
+
}
|
|
71
|
+
const stateResult = await this.getState({ ...space, planningId: input.planningId });
|
|
72
|
+
const state = stateResult.state ?? normalizeState({}, space.projectId, space.knowledgeSpaceId);
|
|
73
|
+
return evaluateProjectPlanningReadiness(state);
|
|
74
|
+
}
|
|
75
|
+
async listDecisions(input = {}) {
|
|
76
|
+
await this.store.init();
|
|
77
|
+
const space = this.resolveSpace(input);
|
|
78
|
+
const decisions = this.sourcesForSpace(space.knowledgeSpaceId)
|
|
79
|
+
.filter((source) => artifactKind(source) === 'decision')
|
|
80
|
+
.map(readDecision)
|
|
81
|
+
.filter((decision) => decision !== null)
|
|
82
|
+
.sort((a, b) => (b.updatedAt ?? b.createdAt ?? 0) - (a.updatedAt ?? a.createdAt ?? 0));
|
|
83
|
+
return {
|
|
84
|
+
ok: true,
|
|
85
|
+
projectId: space.projectId,
|
|
86
|
+
knowledgeSpaceId: space.knowledgeSpaceId,
|
|
87
|
+
decisions,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async recordDecision(input) {
|
|
91
|
+
await this.store.init();
|
|
92
|
+
const space = this.resolveSpace(input);
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
const decision = {
|
|
95
|
+
id: input.decision.id ?? stablePlanningId('decision', input.decision.title),
|
|
96
|
+
title: input.decision.title.trim(),
|
|
97
|
+
...(input.decision.context ? { context: input.decision.context } : {}),
|
|
98
|
+
decision: input.decision.decision.trim(),
|
|
99
|
+
...(input.decision.alternatives ? { alternatives: [...input.decision.alternatives] } : {}),
|
|
100
|
+
...(input.decision.reasoning ? { reasoning: input.decision.reasoning } : {}),
|
|
101
|
+
...(input.decision.consequences ? { consequences: [...input.decision.consequences] } : {}),
|
|
102
|
+
status: input.decision.status ?? 'accepted',
|
|
103
|
+
createdAt: input.decision.createdAt ?? now,
|
|
104
|
+
updatedAt: now,
|
|
105
|
+
...(input.decision.metadata ? { metadata: { ...input.decision.metadata } } : {}),
|
|
106
|
+
};
|
|
107
|
+
const source = await this.upsertArtifactSource(space, 'decision', decision.id, decision);
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
projectId: space.projectId,
|
|
111
|
+
knowledgeSpaceId: space.knowledgeSpaceId,
|
|
112
|
+
decision,
|
|
113
|
+
source,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async getLanguage(input = {}) {
|
|
117
|
+
await this.store.init();
|
|
118
|
+
const space = this.resolveSpace(input);
|
|
119
|
+
const source = this.getArtifactSource(space.knowledgeSpaceId, 'language', 'current');
|
|
120
|
+
const language = source ? readLanguage(source) : null;
|
|
121
|
+
return {
|
|
122
|
+
ok: true,
|
|
123
|
+
projectId: space.projectId,
|
|
124
|
+
knowledgeSpaceId: space.knowledgeSpaceId,
|
|
125
|
+
language,
|
|
126
|
+
...(source ? { source } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async upsertLanguage(input) {
|
|
130
|
+
await this.store.init();
|
|
131
|
+
const space = this.resolveSpace(input);
|
|
132
|
+
const now = Date.now();
|
|
133
|
+
const existing = await this.getLanguage(space);
|
|
134
|
+
const language = {
|
|
135
|
+
projectId: space.projectId,
|
|
136
|
+
knowledgeSpaceId: space.knowledgeSpaceId,
|
|
137
|
+
terms: input.language.terms ? [...input.language.terms] : existing.language?.terms ?? [],
|
|
138
|
+
ambiguities: input.language.ambiguities ? [...input.language.ambiguities] : existing.language?.ambiguities ?? [],
|
|
139
|
+
...(input.language.examples ? { examples: [...input.language.examples] } : existing.language?.examples ? { examples: existing.language.examples } : {}),
|
|
140
|
+
updatedAt: now,
|
|
141
|
+
metadata: {
|
|
142
|
+
...(existing.language?.metadata ?? {}),
|
|
143
|
+
...(input.language.metadata ?? {}),
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const source = await this.upsertArtifactSource(space, 'language', 'current', language);
|
|
147
|
+
return {
|
|
148
|
+
ok: true,
|
|
149
|
+
projectId: space.projectId,
|
|
150
|
+
knowledgeSpaceId: space.knowledgeSpaceId,
|
|
151
|
+
language,
|
|
152
|
+
source,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
resolveSpace(input = {}) {
|
|
156
|
+
return resolveProjectPlanningSpace(input, this.defaultProjectId);
|
|
157
|
+
}
|
|
158
|
+
sourcesForSpace(spaceId) {
|
|
159
|
+
return this.store.listSources(10_000).filter((source) => {
|
|
160
|
+
const metadata = source.metadata ?? {};
|
|
161
|
+
return metadata.projectPlanning === true && metadata.knowledgeSpaceId === spaceId;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
getArtifactSource(spaceId, kind, id) {
|
|
165
|
+
const sourceId = projectPlanningSourceId(spaceId, kind, id);
|
|
166
|
+
return this.store.getSource(sourceId) ?? this.store.getSourceByCanonicalUri(projectPlanningCanonicalUri(spaceId, kind, id));
|
|
167
|
+
}
|
|
168
|
+
async upsertArtifactSource(space, kind, id, value) {
|
|
169
|
+
const spaceId = space.knowledgeSpaceId;
|
|
170
|
+
return this.store.upsertSource({
|
|
171
|
+
id: projectPlanningSourceId(spaceId, kind, id),
|
|
172
|
+
connectorId: PROJECT_PLANNING_CONNECTOR_ID,
|
|
173
|
+
sourceType: 'dataset',
|
|
174
|
+
title: titleForArtifact(kind, value),
|
|
175
|
+
canonicalUri: projectPlanningCanonicalUri(spaceId, kind, id),
|
|
176
|
+
summary: projectPlanningArtifactSummary(kind, value),
|
|
177
|
+
tags: [PROJECT_PLANNING_TAG, `project-planning:${kind}`],
|
|
178
|
+
status: 'indexed',
|
|
179
|
+
metadata: knowledgeSpaceMetadata(spaceId, {
|
|
180
|
+
projectPlanning: true,
|
|
181
|
+
planningArtifactKind: kind,
|
|
182
|
+
planningArtifactId: id,
|
|
183
|
+
projectId: space.projectId,
|
|
184
|
+
value,
|
|
185
|
+
}),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function normalizeState(input, projectId, knowledgeSpaceId) {
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
const id = normalizePlanningId(input.id);
|
|
192
|
+
return {
|
|
193
|
+
id,
|
|
194
|
+
projectId,
|
|
195
|
+
knowledgeSpaceId,
|
|
196
|
+
goal: typeof input.goal === 'string' ? input.goal : '',
|
|
197
|
+
...(typeof input.scope === 'string' ? { scope: input.scope } : {}),
|
|
198
|
+
knownContext: arrayOfObjectsOrStrings(input.knownContext),
|
|
199
|
+
openQuestions: arrayOfObjects(input.openQuestions),
|
|
200
|
+
answeredQuestions: arrayOfObjects(input.answeredQuestions),
|
|
201
|
+
decisions: arrayOfObjects(input.decisions),
|
|
202
|
+
assumptions: arrayOfObjectsOrStrings(input.assumptions),
|
|
203
|
+
constraints: arrayOfObjectsOrStrings(input.constraints),
|
|
204
|
+
risks: arrayOfObjectsOrStrings(input.risks),
|
|
205
|
+
tasks: arrayOfObjects(input.tasks),
|
|
206
|
+
dependencies: arrayOfObjects(input.dependencies),
|
|
207
|
+
verificationGates: arrayOfObjects(input.verificationGates),
|
|
208
|
+
agentAssignments: arrayOfObjects(input.agentAssignments),
|
|
209
|
+
readiness: input.readiness ?? 'not-ready',
|
|
210
|
+
executionApproved: input.executionApproved === true,
|
|
211
|
+
createdAt: typeof input.createdAt === 'number' ? input.createdAt : now,
|
|
212
|
+
updatedAt: now,
|
|
213
|
+
metadata: readPlanningMetadataObject(input.metadata),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function normalizePlanningId(value) {
|
|
217
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
218
|
+
? stablePlanningId('plan', value)
|
|
219
|
+
: 'current';
|
|
220
|
+
}
|
|
221
|
+
function arrayOfObjects(value) {
|
|
222
|
+
if (!Array.isArray(value))
|
|
223
|
+
return [];
|
|
224
|
+
return value.filter((entry) => !!entry && typeof entry === 'object' && !Array.isArray(entry));
|
|
225
|
+
}
|
|
226
|
+
function arrayOfObjectsOrStrings(value) {
|
|
227
|
+
if (!Array.isArray(value))
|
|
228
|
+
return [];
|
|
229
|
+
return value.flatMap((entry) => {
|
|
230
|
+
if (typeof entry === 'string')
|
|
231
|
+
return [entry];
|
|
232
|
+
if (entry && typeof entry === 'object' && 'text' in entry && typeof entry.text === 'string')
|
|
233
|
+
return [entry.text];
|
|
234
|
+
return [];
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function artifactKind(source) {
|
|
238
|
+
const kind = source.metadata.planningArtifactKind;
|
|
239
|
+
return kind === 'state' || kind === 'decision' || kind === 'language' ? kind : null;
|
|
240
|
+
}
|
|
241
|
+
function readState(source) {
|
|
242
|
+
const value = source.metadata.value;
|
|
243
|
+
return value && typeof value === 'object' ? value : null;
|
|
244
|
+
}
|
|
245
|
+
function readDecision(source) {
|
|
246
|
+
const value = source.metadata.value;
|
|
247
|
+
return value && typeof value === 'object' ? value : null;
|
|
248
|
+
}
|
|
249
|
+
function readLanguage(source) {
|
|
250
|
+
const value = source.metadata.value;
|
|
251
|
+
return value && typeof value === 'object' ? value : null;
|
|
252
|
+
}
|
|
253
|
+
function titleForArtifact(kind, value) {
|
|
254
|
+
if (kind === 'state')
|
|
255
|
+
return `Planning: ${value.goal || 'Current plan'}`;
|
|
256
|
+
if (kind === 'decision')
|
|
257
|
+
return `Decision: ${value.title}`;
|
|
258
|
+
return 'Project Language';
|
|
259
|
+
}
|