engineering-memory 1.11.8 → 1.11.10
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/dispatcher/sections.mjs +7 -3
- package/install/codex-approval.mjs +457 -0
- package/install/files.mjs +119 -9
- package/install/installer.mjs +44 -5
- package/install/launcher.mjs +95 -0
- package/package.json +1 -1
- package/runtime/build.json +1 -1
- package/runtime/dist/src/config.js +4 -0
- package/runtime/dist/src/git/git-inspector.js +82 -3
- package/runtime/dist/src/git/verification-gate.js +30 -17
- package/runtime/dist/src/index.js +25 -81
- package/runtime/dist/src/mcp/onboarding-tools.js +3 -16
- package/runtime/dist/src/mcp/questionnaire-tools.js +99 -15
- package/runtime/dist/src/mcp/server.js +14 -7
- package/runtime/dist/src/mcp/tool-annotations.js +111 -0
- package/runtime/dist/src/mcp/tool-definitions.js +148 -28
- package/runtime/dist/src/mcp/tool-host.js +12 -0
- package/runtime/dist/src/mcp/worktree-tools.js +323 -236
- package/runtime/dist/src/project/repository.js +11 -5
- package/runtime/dist/src/runtime/active-context-store.js +3 -0
- package/runtime/dist/src/runtime/branch-preferences.js +0 -19
- package/runtime/dist/src/runtime/bridge-service.js +1163 -231
- package/runtime/dist/src/runtime/create-bridge-service.js +82 -0
- package/runtime/dist/src/runtime/offline-outbox.js +33 -158
- package/runtime/dist/src/runtime/phase-timer.js +26 -0
- package/runtime/dist/src/runtime/privacy-detector.js +263 -0
- package/runtime/dist/src/runtime/questionnaire-store.js +210 -49
- package/runtime/dist/src/runtime/recovery-error.js +3 -1
- package/runtime/dist/src/runtime/runtime-entry.js +24 -0
- package/runtime/dist/src/runtime/runtime-host.js +263 -0
- package/runtime/dist/src/runtime/task-branch-store.js +17 -1
- package/runtime/dist/src/runtime/task-start.js +281 -0
- package/runtime/dist/src/runtime/worktree-pool.js +340 -24
- package/runtime/dist/src/utilities/process.js +1 -0
- package/skill/SKILL.md +3 -3
- package/skill/references/lifecycle.md +44 -19
- package/skill/references/memory-updates.md +9 -3
- package/skill/references/project-onboarding.md +19 -2
- package/skill/references/questionnaires.md +77 -15
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { acceptedContent, CLIENT_CAPABILITIES_META_KEY, inputRequired, inputResponse, } from '@modelcontextprotocol/server';
|
|
2
2
|
import { questionnaireAnswerSchema, } from '../runtime/questionnaire-store.js';
|
|
3
|
-
export async function askQuestionnaire(server, service, input, context, previousDefinitions = []) {
|
|
3
|
+
export async function askQuestionnaire(server, service, input, context, previousDefinitions = [], owner) {
|
|
4
4
|
try {
|
|
5
|
-
return await present(server, service, await service.questionnaireAsk(input, previousDefinitions), input.repoRoot, context, input.preparation);
|
|
5
|
+
return await present(server, service, await service.questionnaireAsk(input, previousDefinitions, owner), input.repoRoot, context, input.preparation, input.presentation);
|
|
6
6
|
}
|
|
7
7
|
catch (error) {
|
|
8
8
|
return failure(error);
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
export function answerChoice(result) {
|
|
12
|
+
return answerData(result)?.choice;
|
|
13
|
+
}
|
|
14
|
+
export function answerData(result) {
|
|
15
|
+
const response = result;
|
|
16
|
+
const text = response.content?.find((item) => item.type === 'text')?.text;
|
|
17
|
+
if (!text)
|
|
18
|
+
return undefined;
|
|
19
|
+
const value = JSON.parse(text);
|
|
20
|
+
return value.ok && value.data?.status === 'answered' && value.data.answerAvailable
|
|
21
|
+
? value.data.answer
|
|
22
|
+
: undefined;
|
|
23
|
+
}
|
|
11
24
|
export async function resumeQuestionnaire(server, service, input, context) {
|
|
12
25
|
try {
|
|
13
26
|
return await present(server, service, await service.questionnaireResume(input), input.repoRoot, context, input.preparation, input.presentation);
|
|
@@ -19,7 +32,7 @@ export async function resumeQuestionnaire(server, service, input, context) {
|
|
|
19
32
|
export async function answerQuestionnaireFromHost(service, input) {
|
|
20
33
|
try {
|
|
21
34
|
const resolved = await service.questionnaireAnswerFromHost(input);
|
|
22
|
-
return answered(resolved.record, resolved.answer, resolved.replayed);
|
|
35
|
+
return answered(resolved.record, resolved.answer, resolved.replayed, resolved.retry);
|
|
23
36
|
}
|
|
24
37
|
catch (error) {
|
|
25
38
|
return failure(error);
|
|
@@ -33,8 +46,11 @@ async function present(server, service, record, repoRoot, context, preparation,
|
|
|
33
46
|
answerAvailable: false,
|
|
34
47
|
nextAction: 'This decision was explicitly withdrawn. Do not continue dependent work. Use a new questionnaireId or onboarding requestKey only if the user resumes that work.',
|
|
35
48
|
});
|
|
36
|
-
if (record.status === 'answered')
|
|
37
|
-
|
|
49
|
+
if (record.status === 'answered') {
|
|
50
|
+
if (inputResponse(context.mcpReq.inputResponses, record.requestKey).kind !== 'missing')
|
|
51
|
+
consumedResponses(context).add(record.requestKey);
|
|
52
|
+
return answered(record, record.questions ? record.answers : record.answer, true);
|
|
53
|
+
}
|
|
38
54
|
if (context.mcpReq.signal.aborted)
|
|
39
55
|
return pending(record, 'interrupted');
|
|
40
56
|
if (presentation === 'host_native')
|
|
@@ -42,22 +58,27 @@ async function present(server, service, record, repoRoot, context, preparation,
|
|
|
42
58
|
const responses = context.mcpReq.inputResponses;
|
|
43
59
|
const response = inputResponse(responses, record.requestKey);
|
|
44
60
|
if (response.kind === 'elicit' && response.action === 'accept') {
|
|
45
|
-
const
|
|
61
|
+
const content = acceptedContent(responses, record.requestKey);
|
|
62
|
+
const parsed = questionnaireAnswerSchema(record).safeParse(record.questions && content ? formAnswers(record, content) : content);
|
|
63
|
+
const answer = parsed.success ? parsed.data : undefined;
|
|
46
64
|
if (!answer)
|
|
47
65
|
return pending(record, 'invalid_response');
|
|
66
|
+
consumedResponses(context).add(record.requestKey);
|
|
48
67
|
const resolved = await service.questionnaireAccept({
|
|
49
68
|
repoRoot,
|
|
50
69
|
preparation,
|
|
51
70
|
questionnaireId: record.questionnaireId,
|
|
52
71
|
requestKey: record.requestKey,
|
|
53
72
|
scope: record.scope,
|
|
54
|
-
answer,
|
|
73
|
+
answer: answer,
|
|
55
74
|
});
|
|
56
75
|
return answered(resolved.record, resolved.answer, resolved.replayed);
|
|
57
76
|
}
|
|
58
77
|
if (response.kind === 'elicit')
|
|
59
78
|
return pending(record, response.action);
|
|
60
|
-
if (
|
|
79
|
+
if (response.kind !== 'missing' ||
|
|
80
|
+
context.mcpReq.droppedInputResponseKeys?.length ||
|
|
81
|
+
Object.keys(responses ?? {}).some((key) => !consumedResponses(context).has(key)))
|
|
61
82
|
return pending(record, 'missing_or_stale_response');
|
|
62
83
|
const envelope = context.mcpReq.envelope;
|
|
63
84
|
const capabilities = envelope?.[CLIENT_CAPABILITIES_META_KEY] ?? server.server.getClientCapabilities();
|
|
@@ -65,6 +86,35 @@ async function present(server, service, record, repoRoot, context, preparation,
|
|
|
65
86
|
if (!elicitation || (!elicitation.form && elicitation.url !== undefined))
|
|
66
87
|
return pending(record, 'native_form_unavailable');
|
|
67
88
|
const copy = questionnaireCopy[record.language ?? 'en'];
|
|
89
|
+
if (record.questions) {
|
|
90
|
+
const properties = {};
|
|
91
|
+
const required = [];
|
|
92
|
+
for (const sub of record.questions) {
|
|
93
|
+
properties[sub.id] = {
|
|
94
|
+
type: 'string',
|
|
95
|
+
title: sub.message,
|
|
96
|
+
enum: sub.options.map((option) => option.id),
|
|
97
|
+
enumNames: sub.options.map((option) => option.label),
|
|
98
|
+
};
|
|
99
|
+
required.push(sub.id);
|
|
100
|
+
if (sub.textField)
|
|
101
|
+
properties[`${sub.id}_text`] = {
|
|
102
|
+
type: 'string',
|
|
103
|
+
title: sub.textField.title,
|
|
104
|
+
minLength: 1,
|
|
105
|
+
maxLength: sub.textField.maxLength,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return inputRequired({
|
|
109
|
+
inputRequests: {
|
|
110
|
+
[record.requestKey]: inputRequired.elicit({
|
|
111
|
+
mode: 'form',
|
|
112
|
+
message: presentationMessage(record),
|
|
113
|
+
requestedSchema: { type: 'object', properties, required },
|
|
114
|
+
}),
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
}
|
|
68
118
|
return inputRequired({
|
|
69
119
|
inputRequests: {
|
|
70
120
|
[record.requestKey]: inputRequired.elicit({
|
|
@@ -112,11 +162,18 @@ async function present(server, service, record, repoRoot, context, preparation,
|
|
|
112
162
|
},
|
|
113
163
|
});
|
|
114
164
|
}
|
|
165
|
+
const consumed = new WeakMap();
|
|
166
|
+
function consumedResponses(context) {
|
|
167
|
+
const keys = consumed.get(context.mcpReq) ?? new Set();
|
|
168
|
+
consumed.set(context.mcpReq, keys);
|
|
169
|
+
return keys;
|
|
170
|
+
}
|
|
115
171
|
const questionnaireCopy = {
|
|
116
172
|
en: {
|
|
117
173
|
pending: 'Choose an answer to resolve this decision. Closing or cancelling the form leaves it pending.',
|
|
118
174
|
freeText: ' For another answer choose Other and fill in text. Free text is not stored and cannot be replayed.',
|
|
119
175
|
storedText: ' The bounded text field is stored as part of this exact decision and is replayed after restart.',
|
|
176
|
+
multi: ' AskUserQuestion takes up to 4 questions per call. Show every question in as few calls as needed, then relay all answers in one questionnaire.answer_from_host.',
|
|
120
177
|
answer: 'Answer',
|
|
121
178
|
other: 'Other',
|
|
122
179
|
otherAnswer: 'Other answer',
|
|
@@ -125,17 +182,33 @@ const questionnaireCopy = {
|
|
|
125
182
|
pending: 'Kararını kaydetmek için bir seçenek seç. Formu kapatmak veya iptal etmek onay sayılmaz; soru beklemede kalır.',
|
|
126
183
|
freeText: ' Farklı bir yanıt için Diğer seçeneğini seçip metin alanını doldur. Serbest yanıt saklanmaz ve daha sonra geri getirilemez.',
|
|
127
184
|
storedText: ' Bu metin alanındaki yanıt kararınla birlikte saklanır; yeniden başlattığında korunur.',
|
|
185
|
+
multi: ' AskUserQuestion tek çağrıda en fazla 4 soru alır. Her soruyu en az çağrıyla göster, sonra tüm yanıtları tek bir questionnaire.answer_from_host çağrısında ilet.',
|
|
128
186
|
answer: 'Yanıt',
|
|
129
187
|
other: 'Diğer',
|
|
130
188
|
otherAnswer: 'Diğer yanıt',
|
|
131
189
|
},
|
|
132
190
|
};
|
|
191
|
+
function formAnswers(record, content) {
|
|
192
|
+
const answers = {};
|
|
193
|
+
for (const sub of record.questions ?? []) {
|
|
194
|
+
if (content[sub.id] === undefined)
|
|
195
|
+
continue;
|
|
196
|
+
const text = content[`${sub.id}_text`];
|
|
197
|
+
answers[sub.id] = {
|
|
198
|
+
choice: content[sub.id],
|
|
199
|
+
...(text !== undefined && sub.textField?.requiredForChoice === content[sub.id]
|
|
200
|
+
? { text }
|
|
201
|
+
: {}),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return answers;
|
|
205
|
+
}
|
|
133
206
|
function presentationMessage(record) {
|
|
134
207
|
const notice = /^memory-batch-[a-f0-9]{64}-[0-9]+$/.test(record.questionnaireId)
|
|
135
208
|
? 'For an unversioned project proposal, approval updates legacy memory only. Source-aware tasks cannot read it until an inspection explicitly retains it. Check each sourceApplicabilityNotice in memory.list_proposals before approving.'
|
|
136
209
|
: undefined;
|
|
137
210
|
const copy = questionnaireCopy[record.language ?? 'en'];
|
|
138
|
-
return `${record.message}${notice ? `\n\n${notice}` : ''}\n\n${copy.pending}${record.allowFreeText ? copy.freeText : ''}${record.textField ? copy.storedText : ''}`;
|
|
211
|
+
return `${record.message}${notice ? `\n\n${notice}` : ''}\n\n${copy.pending}${record.allowFreeText ? copy.freeText : ''}${record.textField ? copy.storedText : ''}${record.questions ? copy.multi : ''}`;
|
|
139
212
|
}
|
|
140
213
|
function pending(record, reason) {
|
|
141
214
|
return result({
|
|
@@ -149,25 +222,36 @@ function pending(record, reason) {
|
|
|
149
222
|
requestKey: record.requestKey,
|
|
150
223
|
contentHash: record.contentHash,
|
|
151
224
|
message: presentationMessage(record),
|
|
152
|
-
options: record.options,
|
|
153
225
|
language: record.language ?? 'en',
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
226
|
+
...(record.questions
|
|
227
|
+
? {
|
|
228
|
+
questions: record.questions,
|
|
229
|
+
...(record.terminalChoices ? { terminalChoices: record.terminalChoices } : {}),
|
|
230
|
+
}
|
|
231
|
+
: {
|
|
232
|
+
options: record.options,
|
|
233
|
+
allowFreeText: record.allowFreeText,
|
|
234
|
+
...(record.textField ? { textField: record.textField } : {}),
|
|
235
|
+
}),
|
|
236
|
+
instructions: record.questions
|
|
237
|
+
? 'Only if the host permits a blocking native control for this decision, display every question, its options and notices in AskUserQuestion or request_user_input, one call at a time if needed. Relay every answer together in one questionnaire.answer_from_host call as `answers`, keyed by question id. Closing, declining, timeout, prose consent and missing answers are not native answers for any question. Do not reopen a dismissed question in a loop or label decline as proof of host incompatibility. The relay records agent-reported provenance, not MCP transport attestation. Retry the owning operation after acceptance; its authority and version checks still apply.'
|
|
238
|
+
: 'Only if the host permits a blocking native control for this decision, display this exact question, all options and notices in AskUserQuestion or request_user_input. Relay only the actual returned answer with these unchanged bindings. Closing, declining, timeout, prose consent and missing answers are not native answers. Do not reopen a dismissed question in a loop or label decline as proof of host incompatibility. The relay records agent-reported provenance, not MCP transport attestation. Retry the owning operation after acceptance; its authority and version checks still apply.',
|
|
157
239
|
},
|
|
158
240
|
nextAction: reason === 'native_form_unavailable'
|
|
159
241
|
? 'This host does not advertise native MCP forms. The decision remains pending. Explain the limitation and continue only independently authorized work. Use hostFallback only with a permitted blocking native control; otherwise keep pending. Never substitute an asynchronous question.'
|
|
160
242
|
: 'The decision remains pending without expiry. Call questionnaire.resume with this questionnaireId to show the same question again. Do not infer an answer or continue dependent work.',
|
|
161
243
|
});
|
|
162
244
|
}
|
|
163
|
-
function answered(record, answer, replayed) {
|
|
245
|
+
function answered(record, answer, replayed, retry) {
|
|
246
|
+
const key = record.questions ? 'answers' : 'answer';
|
|
164
247
|
return result({
|
|
165
248
|
questionnaireId: record.questionnaireId,
|
|
166
249
|
status: 'answered',
|
|
167
250
|
answerAvailable: answer !== undefined,
|
|
168
|
-
...(answer ? { answer } : {}),
|
|
251
|
+
...(answer ? { [key]: answer } : {}),
|
|
169
252
|
replayed,
|
|
170
253
|
...(record.answerSource ? { answerSource: record.answerSource } : {}),
|
|
254
|
+
...(retry ? { retry } : {}),
|
|
171
255
|
...(!answer
|
|
172
256
|
? {
|
|
173
257
|
nextAction: 'The free-text answer was accepted earlier but was not stored for privacy. It cannot be recovered or inferred. Use the original answer if still present in this conversation; otherwise collect it again through a new questionnaire or supported native input before dependent work.',
|
|
@@ -2,17 +2,24 @@ import { registerWorktreeTools } from './worktree-tools.js';
|
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
3
3
|
import { registerOnboardingTools } from './onboarding-tools.js';
|
|
4
4
|
import { registerEngineeringMemoryTools, registerQuestionnaireTools } from './tool-definitions.js';
|
|
5
|
-
export
|
|
6
|
-
|
|
5
|
+
export const serverInstructions = 'Start with session.entry and follow the returned lifecycle. Do not block independent task work on memory.propose_revision drafting, submission or approval. If the host permits background agents or concurrent calls, dispatch independent proposals and continue useful work; otherwise checkpoint the draft and defer submission until needed. Checkpoint, correction, reconciliation, self-review and scaffold receipts use the durable local outbox; after deliveryStatus pending, continue independent work without polling or resending. Inventory upload, claim-bound evidence submission and test-result reporting may overlap independent analysis using host-permitted concurrent calls. Wait only when the next operation needs the result; required task deliveries must settle before verification. Preserve task/source identity and native user approval; never treat pending work as verified.';
|
|
6
|
+
export function createMcpServer() {
|
|
7
|
+
return new McpServer({
|
|
7
8
|
name: 'engineering-memory',
|
|
8
9
|
version: '0.1.0',
|
|
9
10
|
}, {
|
|
10
|
-
instructions:
|
|
11
|
+
instructions: serverInstructions,
|
|
11
12
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
}
|
|
14
|
+
export function registerAllTools(host, service) {
|
|
15
|
+
registerEngineeringMemoryTools(host, service);
|
|
16
|
+
registerQuestionnaireTools(host, service);
|
|
17
|
+
registerOnboardingTools(host, service);
|
|
18
|
+
registerWorktreeTools(host, service);
|
|
19
|
+
}
|
|
20
|
+
export function createEngineeringMemoryServer(service) {
|
|
21
|
+
const server = createMcpServer();
|
|
22
|
+
registerAllTools(server, service);
|
|
16
23
|
return server;
|
|
17
24
|
}
|
|
18
25
|
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const read = { readOnlyHint: true, openWorldHint: false }; // receipts and registry upkeep still read
|
|
2
|
+
const write = {
|
|
3
|
+
readOnlyHint: false,
|
|
4
|
+
destructiveHint: false,
|
|
5
|
+
openWorldHint: false,
|
|
6
|
+
};
|
|
7
|
+
const repeatableWrite = { ...write, idempotentHint: true };
|
|
8
|
+
const destructive = {
|
|
9
|
+
readOnlyHint: false,
|
|
10
|
+
destructiveHint: true,
|
|
11
|
+
openWorldHint: false,
|
|
12
|
+
};
|
|
13
|
+
const outward = {
|
|
14
|
+
readOnlyHint: false,
|
|
15
|
+
destructiveHint: false,
|
|
16
|
+
openWorldHint: true,
|
|
17
|
+
};
|
|
18
|
+
export const toolAnnotations = {
|
|
19
|
+
'project.git_preferences': read,
|
|
20
|
+
'project.set_git_preferences': write,
|
|
21
|
+
'worktree.policy': read,
|
|
22
|
+
'worktree.set_policy': write,
|
|
23
|
+
'worktree.list': read,
|
|
24
|
+
'worktree.reconcile': destructive, // releaseUnowned hands a recovered checkout back
|
|
25
|
+
'task.heartbeat': repeatableWrite,
|
|
26
|
+
'task.pause': write,
|
|
27
|
+
'worktree.release': destructive,
|
|
28
|
+
'audit.list': read,
|
|
29
|
+
'questionnaire.ask': repeatableWrite,
|
|
30
|
+
'questionnaire.resume': repeatableWrite, // records an accepted answer
|
|
31
|
+
'questionnaire.answer_from_host': repeatableWrite,
|
|
32
|
+
'questionnaire.withdraw': destructive,
|
|
33
|
+
'session.entry': read,
|
|
34
|
+
'session.set_decision': repeatableWrite,
|
|
35
|
+
'session.decline_update': repeatableWrite,
|
|
36
|
+
'session.answer_shadow_notice': repeatableWrite,
|
|
37
|
+
'session.bootstrap': write,
|
|
38
|
+
'session.resume': write,
|
|
39
|
+
'context.prepare_change': write,
|
|
40
|
+
'context.refresh': write,
|
|
41
|
+
'project.inspect': read,
|
|
42
|
+
'project.onboard': write,
|
|
43
|
+
'project.onboarding_status': read,
|
|
44
|
+
'project.rework_plan': write,
|
|
45
|
+
'project.initialize': write,
|
|
46
|
+
'memory.source': read,
|
|
47
|
+
'memory.sync_start': repeatableWrite,
|
|
48
|
+
'memory.sync_status': read,
|
|
49
|
+
'memory.sync_inventory': write,
|
|
50
|
+
'memory.sync_plan': write,
|
|
51
|
+
'memory.sync_next': write,
|
|
52
|
+
'memory.sync_submit': write,
|
|
53
|
+
'memory.sync_reopen': write,
|
|
54
|
+
'memory.sync_delta': read,
|
|
55
|
+
'memory.sync_verify': repeatableWrite,
|
|
56
|
+
'memory.catalog': read,
|
|
57
|
+
'memory.sources': read,
|
|
58
|
+
'memory.source_targets': read,
|
|
59
|
+
'memory.publish_task': write,
|
|
60
|
+
'memory.read_revisions': read,
|
|
61
|
+
'memory.review_batch': write,
|
|
62
|
+
'memory.query': read,
|
|
63
|
+
'memory.history': read,
|
|
64
|
+
'memory.propose_revision': write,
|
|
65
|
+
'memory.list_proposals': read,
|
|
66
|
+
'memory.review_proposal': write,
|
|
67
|
+
'task.checkpoint': write,
|
|
68
|
+
'task.record_correction': write,
|
|
69
|
+
'task.self_review': write,
|
|
70
|
+
'task.reconcile': write,
|
|
71
|
+
'task.resolve_pending_delivery': destructive, // discards a pending delivery
|
|
72
|
+
'task.verify': write,
|
|
73
|
+
'task.close': write,
|
|
74
|
+
'task.abandon': destructive,
|
|
75
|
+
'task.branch': outward, // fetches the chosen base from the Git remote
|
|
76
|
+
'architecture.plan': read,
|
|
77
|
+
'architecture.module': read,
|
|
78
|
+
'architecture.record_application': write,
|
|
79
|
+
'organization.list': read,
|
|
80
|
+
'organization.create': write,
|
|
81
|
+
'organization.update': write,
|
|
82
|
+
'organization.member_list': read,
|
|
83
|
+
'organization.member_upsert': write,
|
|
84
|
+
'project.setup': write,
|
|
85
|
+
'project.list': read,
|
|
86
|
+
'project.resolve': repeatableWrite, // binds an explicitly selected project
|
|
87
|
+
'project.archive': destructive,
|
|
88
|
+
'project.restore': write,
|
|
89
|
+
'project.member_add': write,
|
|
90
|
+
'project.member_list': read,
|
|
91
|
+
'work_item.create': write,
|
|
92
|
+
'work_item.update': write,
|
|
93
|
+
'work_item.archive': destructive,
|
|
94
|
+
'work_item.restore': write,
|
|
95
|
+
'work_item.runs': read,
|
|
96
|
+
'work_item.record_test': write,
|
|
97
|
+
'work_item.test_results': read,
|
|
98
|
+
'work_item.plan': read,
|
|
99
|
+
'work_item.confirm_plan': write,
|
|
100
|
+
'project.update': write,
|
|
101
|
+
'work_item.list': read,
|
|
102
|
+
'work_item.get': read,
|
|
103
|
+
'project.clone': outward,
|
|
104
|
+
'project.link': write,
|
|
105
|
+
'project.unlink': destructive,
|
|
106
|
+
'project.links': read,
|
|
107
|
+
'auth.status': read,
|
|
108
|
+
'auth.signin_browser': outward,
|
|
109
|
+
'auth.logout': destructive,
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=tool-annotations.js.map
|