@unson/brainbase-mcp 0.4.0 → 0.4.1

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.
@@ -25,8 +25,8 @@
25
25
  },
26
26
  {
27
27
  "path": "README.md",
28
- "sha256": "7cd5721b9a932f5b43dd01557dfbcd8ea73563d5d23337fabdd322367d678e28"
28
+ "sha256": "a263114611c100feb75f9d35ad793593eafb7dea217d846a99efacc06c7efa24"
29
29
  }
30
30
  ],
31
- "digest": "fd5587b97cf343290c7d6c78f365d2d78bef668b04578e5ab55c67886ba0e827"
31
+ "digest": "e97cefb8dadd451d5929c266376f3774fa84dcdde412e5ff3305b9b70d580ecd"
32
32
  }
@@ -0,0 +1,143 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://brainbase.pages.dev/contracts/public-message-candidate.schema.json",
4
+ "title": "Brainbase approved public-message candidate",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version",
9
+ "candidate_id",
10
+ "status",
11
+ "source",
12
+ "approval",
13
+ "copy"
14
+ ],
15
+ "properties": {
16
+ "schema_version": {
17
+ "const": "1.0.0"
18
+ },
19
+ "candidate_id": {
20
+ "type": "string",
21
+ "pattern": "^[a-z0-9][a-z0-9._-]{2,127}$"
22
+ },
23
+ "status": {
24
+ "const": "approved"
25
+ },
26
+ "source": {
27
+ "type": "object",
28
+ "additionalProperties": false,
29
+ "required": [
30
+ "type",
31
+ "entity_id",
32
+ "snapshot_hash",
33
+ "exported_at",
34
+ "scope"
35
+ ],
36
+ "properties": {
37
+ "type": {
38
+ "const": "brainbase_graph"
39
+ },
40
+ "entity_id": {
41
+ "type": "string",
42
+ "minLength": 1,
43
+ "maxLength": 200
44
+ },
45
+ "entity_version": {
46
+ "type": "integer",
47
+ "minimum": 1
48
+ },
49
+ "snapshot_hash": {
50
+ "type": "string",
51
+ "pattern": "^sha256:[0-9a-f]{64}$"
52
+ },
53
+ "exported_at": {
54
+ "type": "string",
55
+ "format": "date-time"
56
+ },
57
+ "scope": {
58
+ "type": "string",
59
+ "minLength": 1,
60
+ "maxLength": 100
61
+ }
62
+ }
63
+ },
64
+ "approval": {
65
+ "type": "object",
66
+ "additionalProperties": false,
67
+ "required": [
68
+ "status",
69
+ "approved_by",
70
+ "approved_at",
71
+ "reason"
72
+ ],
73
+ "properties": {
74
+ "status": {
75
+ "const": "approved"
76
+ },
77
+ "approved_by": {
78
+ "type": "string",
79
+ "minLength": 1,
80
+ "maxLength": 100
81
+ },
82
+ "approved_at": {
83
+ "type": "string",
84
+ "format": "date-time"
85
+ },
86
+ "reason": {
87
+ "type": "string",
88
+ "minLength": 1,
89
+ "maxLength": 1000
90
+ }
91
+ }
92
+ },
93
+ "copy": {
94
+ "$ref": "#/$defs/copy"
95
+ }
96
+ },
97
+ "$defs": {
98
+ "copy": {
99
+ "type": "object",
100
+ "additionalProperties": false,
101
+ "required": [
102
+ "headline",
103
+ "short_definition",
104
+ "definition",
105
+ "human_role",
106
+ "ai_role",
107
+ "package_description"
108
+ ],
109
+ "properties": {
110
+ "headline": {
111
+ "type": "string",
112
+ "minLength": 1,
113
+ "maxLength": 80
114
+ },
115
+ "short_definition": {
116
+ "type": "string",
117
+ "minLength": 1,
118
+ "maxLength": 160
119
+ },
120
+ "definition": {
121
+ "type": "string",
122
+ "minLength": 1,
123
+ "maxLength": 400
124
+ },
125
+ "human_role": {
126
+ "type": "string",
127
+ "minLength": 1,
128
+ "maxLength": 200
129
+ },
130
+ "ai_role": {
131
+ "type": "string",
132
+ "minLength": 1,
133
+ "maxLength": 240
134
+ },
135
+ "package_description": {
136
+ "type": "string",
137
+ "minLength": 1,
138
+ "maxLength": 240
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
package/dist/cli.js CHANGED
@@ -65,7 +65,7 @@ export async function runCli(argv = process.argv.slice(2), io = process) {
65
65
  case 'ontology:migrate':
66
66
  return await ontologyMigrate(parsed, io);
67
67
  case 'judgment:hook':
68
- return await judgmentHook(io);
68
+ return await judgmentHook(parsed, io);
69
69
  case 'judgment:install':
70
70
  return await judgmentInstall(parsed, io);
71
71
  case 'doctor':
@@ -83,12 +83,25 @@ export async function runCli(argv = process.argv.slice(2), io = process) {
83
83
  return 1;
84
84
  }
85
85
  }
86
- async function judgmentHook(io) {
86
+ function judgmentAutonomyOptions(parsed) {
87
+ const rawMode = first(parsed, 'autonomy-mode');
88
+ if (rawMode !== undefined && !['off', 'canary', 'on'].includes(rawMode)) {
89
+ throw new Error('judgment_autonomy_mode_invalid');
90
+ }
91
+ const project = first(parsed, 'autonomy-project')?.trim();
92
+ if (rawMode === 'canary' && !project)
93
+ throw new Error('judgment_autonomy_canary_project_missing');
94
+ return {
95
+ ...(rawMode ? { autonomyMode: rawMode } : {}),
96
+ ...(project ? { autonomyCanaryProjects: [project] } : {})
97
+ };
98
+ }
99
+ async function judgmentHook(parsed, io) {
87
100
  let payload = {};
88
101
  try {
89
102
  const input = await readHookStdin(io.stdin ?? process.stdin);
90
103
  payload = JSON.parse(input || '{}');
91
- write(io, `${JSON.stringify(await processJudgmentHook(payload))}\n`);
104
+ write(io, `${JSON.stringify(await processJudgmentHook(payload, judgmentAutonomyOptions(parsed)))}\n`);
92
105
  }
93
106
  catch (error) {
94
107
  const reason = error instanceof Error ? error.message : String(error);
@@ -106,10 +119,20 @@ async function judgmentInstall(parsed, io) {
106
119
  if (target !== 'codex')
107
120
  throw new Error('judgment:install currently requires --target codex');
108
121
  const cliPath = fileURLToPath(new URL('./cli.js', import.meta.url));
122
+ const autonomy = judgmentAutonomyOptions(parsed);
123
+ const hookArguments = [
124
+ process.execPath,
125
+ cliPath,
126
+ 'judgment:hook',
127
+ ...(autonomy.autonomyMode ? ['--autonomy-mode', autonomy.autonomyMode] : []),
128
+ ...(autonomy.autonomyCanaryProjects?.[0]
129
+ ? ['--autonomy-project', autonomy.autonomyCanaryProjects[0]]
130
+ : [])
131
+ ];
109
132
  const hook = {
110
133
  hooks: [{
111
134
  type: 'command',
112
- command: `${JSON.stringify(process.execPath)} ${JSON.stringify(cliPath)} judgment:hook`,
135
+ command: hookArguments.map((argument) => JSON.stringify(argument)).join(' '),
113
136
  statusMessage: 'brainbase judgment resolver'
114
137
  }]
115
138
  };
@@ -1065,8 +1088,8 @@ function usage() {
1065
1088
  brainbase ontology:show
1066
1089
  brainbase ontology:audit [--dir path] [--ontology-version 0.0.0|1.0.0|2.0.0]
1067
1090
  brainbase ontology:migrate [--dir path] [--write --expected-input-digest digest]
1068
- brainbase judgment:install --target codex [--dry-run] [--output path]
1069
- brainbase judgment:hook
1091
+ brainbase judgment:install --target codex [--autonomy-mode off|canary|on] [--autonomy-project code] [--dry-run] [--output path]
1092
+ brainbase judgment:hook [--autonomy-mode off|canary|on] [--autonomy-project code]
1070
1093
  brainbase doctor [--dir path] [--judgment-hooks path]
1071
1094
  `;
1072
1095
  }
@@ -0,0 +1,62 @@
1
+ export type JudgmentAutonomyVerdict = 'continue' | 'human_required' | 'not_applicable';
2
+ export type JudgmentAutonomyReasonCode = 'not_a_human_escalation' | 'routine_reversible_work' | 'semantic_judgment_required' | 'irreversible_external_action' | 'missing_authority_or_secret' | 'material_commitment' | 'owner_value_choice' | 'resolver_selected_clarification' | 'resolver_continue' | 'resolver_human_required';
3
+ export interface JudgmentAutonomyInstructionPatch {
4
+ cancel: readonly string[];
5
+ do_next: readonly string[];
6
+ acceptance_criteria: readonly string[];
7
+ }
8
+ export interface JudgmentAutonomyContext {
9
+ turn_id: string;
10
+ request: string;
11
+ final_answer: string;
12
+ project_code?: string;
13
+ selected_dag_ids: readonly string[];
14
+ }
15
+ export interface JudgmentAutonomyResolverRequest {
16
+ schema_version: 'brainbase-autonomy-resolver-request-v1';
17
+ case_id: string;
18
+ turn_id: string;
19
+ task_request: string;
20
+ proposed_human_question: string;
21
+ project_code?: string;
22
+ selected_dag_ids: readonly string[];
23
+ policy: {
24
+ default_behavior: 'continue';
25
+ complexity_behavior: 'decompose_and_continue';
26
+ human_only: readonly [
27
+ 'irreversible_external_action',
28
+ 'missing_authority_or_secret',
29
+ 'material_commitment',
30
+ 'new_owner_value_choice'
31
+ ];
32
+ };
33
+ }
34
+ export interface JudgmentAutonomyResolverDecision {
35
+ schema_version: 'brainbase-autonomy-resolver-decision-v1';
36
+ case_id: string;
37
+ verdict: 'continue' | 'human_required';
38
+ reason_code: string;
39
+ reason: string;
40
+ basis: ReadonlyArray<{
41
+ entity_id: string;
42
+ application: string;
43
+ }>;
44
+ instruction_patch?: JudgmentAutonomyInstructionPatch;
45
+ human_question?: string;
46
+ }
47
+ export type JudgmentAutonomyResolver = (request: JudgmentAutonomyResolverRequest) => Promise<JudgmentAutonomyResolverDecision>;
48
+ export interface JudgmentAutonomyEvaluation {
49
+ schema_version: 'brainbase-autonomy-evaluation-v1';
50
+ case_id: string;
51
+ verdict: JudgmentAutonomyVerdict;
52
+ reason_code: JudgmentAutonomyReasonCode;
53
+ reason: string;
54
+ question: string | null;
55
+ source: 'deterministic' | 'same_codex' | 'independent_resolver';
56
+ instruction_patch: JudgmentAutonomyInstructionPatch | null;
57
+ resolver_decision: JudgmentAutonomyResolverDecision | null;
58
+ }
59
+ export declare function visibleAnswerBody(answer: string): string;
60
+ export declare function extractHumanDecisionQuestion(answer: string): string | null;
61
+ export declare function evaluateJudgmentAutonomy(context: JudgmentAutonomyContext, resolver?: JudgmentAutonomyResolver): Promise<JudgmentAutonomyEvaluation>;
62
+ export declare function renderJudgmentAutonomyContinuation(evaluation: JudgmentAutonomyEvaluation): string;
@@ -0,0 +1,301 @@
1
+ import { createHash } from 'node:crypto';
2
+ const AUDIT_LINE = /^(?:🧠 |📚 Brainbase|⚠️ (?:Brainbase|判断))/u;
3
+ const QUESTION_END = /(?:[??]|(?:選んで|教えて|確認して|承認して)(?:ください|下さい)?[。.]?)$/u;
4
+ const HUMAN_QUESTION_PATTERNS = [
5
+ /(?:しても|して|進めても|実行しても|変更しても|修正しても|作成しても)(?:よい|いい|良い)ですか/u,
6
+ /(?:しますか|進めますか|実行しますか|変更しますか|修正しますか|作成しますか)/u,
7
+ /(?:どちら|どれ|何を|どう)(?:に|を|で)?(?:します|進めます|選びます|すべき)か/u,
8
+ /(?:どちら|どの|何の).+(?:ですか|ますか)/u,
9
+ /(?:送って|渡して|共有して)(?:も)?(?:よい|いい|良い)ですか/u,
10
+ /(?:選んで|教えて|確認して|承認して)(?:ください|下さい)/u,
11
+ /(?:送信|送付|公開|投稿|デプロイ|マージ|merge|push|publish|作成|削除|破棄|変更|共有)(?:しても|して)?(?:よい|いい|良い)ですか/iu,
12
+ /(?:送ります|公開します|投稿します|デプロイします|マージします|作成します|削除します|破棄します|変更します|共有します|渡します)か/iu,
13
+ /(?:should i|would you like|do you want|which (?:option|one)|please (?:choose|confirm|approve)|can i)\b/iu
14
+ ];
15
+ const EXTERNAL_ACTION_TERMS = [
16
+ '外部送信', 'メール送信', '送信しますか', '公開しますか', '投稿しますか', '本番デプロイ',
17
+ '本番へデプロイ', 'prを作成', 'pull request', 'issueを作成', 'github issue', 'slack',
18
+ 'メール', 'webhook', '外部api', '外部サービス', '共有しますか', '渡してよい',
19
+ 'mergeしますか', 'pushしますか', 'publish', 'send', 'deploy', 'merge', 'push', 'post'
20
+ ];
21
+ const DESTRUCTIVE_TERMS = [
22
+ '本番データを削除', '本番削除', '本番DB', 'drop table', 'truncate', 'production delete',
23
+ 'データベースを削除', 'dbを削除', '削除しますか', '消去しますか', '不可逆',
24
+ '取り消せない', '破棄', '全削除', '上書き'
25
+ ];
26
+ const PRODUCTION_ACTION_TERMS = [
27
+ '本番環境', '本番で', '本番へ', 'production', 'prod', '商用環境', '顧客環境'
28
+ ];
29
+ const AUTHORITY_CHANGE_TERMS = [
30
+ '権限を変更', '権限付与', '管理者権限', 'owner権限', 'roleを変更', 'rbac',
31
+ 'アクセス権', 'permission change', 'grant access', 'admin role'
32
+ ];
33
+ const SENSITIVE_DATA_TERMS = [
34
+ '個人情報', '機密情報', '顧客情報', '医療情報', '認証情報を送', '秘密情報を送',
35
+ 'personal data', 'pii', 'confidential', 'health data', 'credentialを送'
36
+ ];
37
+ const AUTHORITY_TERMS = [
38
+ '権限がありません', '権限不足', '認証情報', '秘密情報', 'api key', 'credential', 'permission denied',
39
+ 'access denied', 'secretが必要', 'トークンが必要'
40
+ ];
41
+ const MATERIAL_COMMITMENT_TERMS = [
42
+ '購入', '発注', '支払い', '決済', '契約締結', '法的', '金額', '課金', 'purchase', 'payment',
43
+ 'sign the contract', 'legal commitment'
44
+ ];
45
+ const EXPLICIT_NEW_OWNER_VALUE_TERMS = [
46
+ '既存の判断基準では決められない', 'brainbaseに判断基準がない', '新しい価値判断を決める必要',
47
+ 'これまで決めていない方針', 'no existing decision principle', 'new owner value choice is required'
48
+ ];
49
+ const ROUTINE_TERMS = [
50
+ 'テスト', 'lint', 'format', 'フォーマット', 'build', 'ビルド', '型チェック', 'typecheck',
51
+ 'ファイルを読む', 'ファイルを確認', '調査', '検索', 'ログを確認', '差分を確認', 'dry-run',
52
+ 'ローカル', '可逆', '小さな修正', '編集', '実装方法', 'リファクタ', 'read the file',
53
+ 'run the test', 'run tests', 'inspect', 'search', 'local change'
54
+ ];
55
+ const RELEASE_PIPELINE_TERMS = [
56
+ 'prを作成', 'pull request', 'merge', 'マージ', 'push', 'publish', '公開', 'deploy',
57
+ 'デプロイ', '本番展開', '本番反映', 'release', 'リリース'
58
+ ];
59
+ function canonicalJson(value) {
60
+ if (value === null || typeof value === 'boolean' || typeof value === 'string')
61
+ return JSON.stringify(value);
62
+ if (typeof value === 'number') {
63
+ if (!Number.isFinite(value))
64
+ throw new TypeError('canonical JSON only supports finite numbers');
65
+ return JSON.stringify(value);
66
+ }
67
+ if (Array.isArray(value))
68
+ return `[${value.map((entry) => canonicalJson(entry)).join(',')}]`;
69
+ if (value && typeof value === 'object') {
70
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(',')}}`;
71
+ }
72
+ throw new TypeError('canonical JSON only supports JSON values');
73
+ }
74
+ function sha256(value) {
75
+ return createHash('sha256').update(value).digest('hex');
76
+ }
77
+ function containsAny(text, terms) {
78
+ const normalized = text.toLocaleLowerCase();
79
+ return terms.some((term) => normalized.includes(term.toLocaleLowerCase()));
80
+ }
81
+ function requestAuthorizesExternalAction(request, question) {
82
+ if (containsAny(question, RELEASE_PIPELINE_TERMS)) {
83
+ return containsAny(request, RELEASE_PIPELINE_TERMS);
84
+ }
85
+ return false;
86
+ }
87
+ export function visibleAnswerBody(answer) {
88
+ const lines = answer.replaceAll('\r\n', '\n').split('\n');
89
+ while (lines.length > 0 && (lines[0].trim() === '' || AUDIT_LINE.test(lines[0])))
90
+ lines.shift();
91
+ return lines.join('\n').trim();
92
+ }
93
+ export function extractHumanDecisionQuestion(answer) {
94
+ const body = visibleAnswerBody(answer);
95
+ if (!body)
96
+ return null;
97
+ const paragraphs = body.split(/\n\s*\n/u).map((paragraph) => paragraph.trim()).filter(Boolean);
98
+ const candidate = paragraphs.at(-1) ?? body;
99
+ const lines = candidate.split('\n').map((line) => line.trim()).filter(Boolean);
100
+ const question = lines.slice(-3).join(' ').slice(-1200).trim();
101
+ if (!question || !QUESTION_END.test(question))
102
+ return null;
103
+ return HUMAN_QUESTION_PATTERNS.some((pattern) => pattern.test(question)) ? question : null;
104
+ }
105
+ function continuationPatch(question, semantic) {
106
+ return {
107
+ cancel: [`人間への確認質問: ${question}`],
108
+ do_next: semantic ? [
109
+ 'Brainbase MCPから今回の目的、適用可能な判断基準、過去Decision、委任境界を取得する',
110
+ '候補を比較し、既存の人間由来の基準を今回の具体的状況へ適用してOK / NG / 条件付きOKを決める',
111
+ 'NGなら理由だけで止めず、代替行動と完了条件を作業計画へ反映する',
112
+ '安全かつ委任範囲内なら同じCodexターンで調査・実装・検証を続行する'
113
+ ] : [
114
+ '質問対象の通常工程をそのまま実行する',
115
+ '結果を検証し、失敗時は安全な代替手段を試す',
116
+ '同じCodexターンで完了まで続行する'
117
+ ],
118
+ acceptance_criteria: [
119
+ '人間へ同じ確認を再送しない',
120
+ '不可逆な外部操作、実証済みの権限不足、既存基準から決められない新しい価値判断だけを再エスカレーションする',
121
+ '最終回答は既存のBrainbase audit_contractも満たす'
122
+ ]
123
+ };
124
+ }
125
+ function resolverRequest(context, question, caseId) {
126
+ return {
127
+ schema_version: 'brainbase-autonomy-resolver-request-v1',
128
+ case_id: caseId,
129
+ turn_id: context.turn_id,
130
+ task_request: context.request,
131
+ proposed_human_question: question,
132
+ ...(context.project_code ? { project_code: context.project_code } : {}),
133
+ selected_dag_ids: [...context.selected_dag_ids],
134
+ policy: {
135
+ default_behavior: 'continue',
136
+ complexity_behavior: 'decompose_and_continue',
137
+ human_only: [
138
+ 'irreversible_external_action',
139
+ 'missing_authority_or_secret',
140
+ 'material_commitment',
141
+ 'new_owner_value_choice'
142
+ ]
143
+ }
144
+ };
145
+ }
146
+ function validateResolverDecision(decision, caseId) {
147
+ if (decision?.schema_version !== 'brainbase-autonomy-resolver-decision-v1'
148
+ || decision.case_id !== caseId
149
+ || !['continue', 'human_required'].includes(decision.verdict)
150
+ || typeof decision.reason_code !== 'string'
151
+ || !decision.reason_code.trim()
152
+ || typeof decision.reason !== 'string'
153
+ || !decision.reason.trim()
154
+ || !Array.isArray(decision.basis)
155
+ || decision.basis.length === 0
156
+ || !decision.basis.every((entry) => (entry
157
+ && typeof entry.entity_id === 'string'
158
+ && entry.entity_id.trim().length > 0
159
+ && typeof entry.application === 'string'
160
+ && entry.application.trim().length > 0))) {
161
+ throw new Error('judgment_autonomy_resolver_invalid');
162
+ }
163
+ if (decision.verdict === 'continue') {
164
+ const patch = decision.instruction_patch;
165
+ if (!patch
166
+ || !Array.isArray(patch.cancel)
167
+ || !Array.isArray(patch.do_next)
168
+ || patch.do_next.length === 0
169
+ || !Array.isArray(patch.acceptance_criteria)
170
+ || patch.acceptance_criteria.length === 0
171
+ || ![...patch.cancel, ...patch.do_next, ...patch.acceptance_criteria].every((item) => (typeof item === 'string' && item.trim().length > 0))) {
172
+ throw new Error('judgment_autonomy_resolver_instruction_missing');
173
+ }
174
+ const delegatedInstructions = [...patch.cancel, ...patch.do_next, ...patch.acceptance_criteria].join(' ');
175
+ if (containsAny(delegatedInstructions, EXTERNAL_ACTION_TERMS)
176
+ || containsAny(delegatedInstructions, DESTRUCTIVE_TERMS)
177
+ || containsAny(delegatedInstructions, MATERIAL_COMMITMENT_TERMS)) {
178
+ throw new Error('judgment_autonomy_resolver_authority_expansion');
179
+ }
180
+ }
181
+ if (decision.verdict === 'human_required'
182
+ && (typeof decision.human_question !== 'string' || !decision.human_question.trim())) {
183
+ throw new Error('judgment_autonomy_resolver_human_question_missing');
184
+ }
185
+ return decision;
186
+ }
187
+ export async function evaluateJudgmentAutonomy(context, resolver) {
188
+ const question = extractHumanDecisionQuestion(context.final_answer);
189
+ const caseId = sha256(canonicalJson({
190
+ turn_id: context.turn_id,
191
+ request: context.request,
192
+ question,
193
+ project_code: context.project_code ?? null,
194
+ selected_dag_ids: [...context.selected_dag_ids]
195
+ }));
196
+ if (!question) {
197
+ return {
198
+ schema_version: 'brainbase-autonomy-evaluation-v1',
199
+ case_id: caseId,
200
+ verdict: 'not_applicable',
201
+ reason_code: 'not_a_human_escalation',
202
+ reason: 'The final answer does not request a human decision or approval.',
203
+ question: null,
204
+ source: 'deterministic',
205
+ instruction_patch: null,
206
+ resolver_decision: null
207
+ };
208
+ }
209
+ if (context.selected_dag_ids.includes('clarification.v1')) {
210
+ return {
211
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
212
+ verdict: 'human_required', reason_code: 'resolver_selected_clarification',
213
+ reason: 'The accepted Judgment Receipt explicitly selected a missing referent clarification.',
214
+ question, source: 'deterministic', instruction_patch: null, resolver_decision: null
215
+ };
216
+ }
217
+ if (containsAny(question, AUTHORITY_TERMS)) {
218
+ return {
219
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
220
+ verdict: 'human_required', reason_code: 'missing_authority_or_secret',
221
+ reason: 'The agent reports a missing authority or secret that the worker cannot invent.',
222
+ question, source: 'deterministic', instruction_patch: null, resolver_decision: null
223
+ };
224
+ }
225
+ if (containsAny(question, MATERIAL_COMMITMENT_TERMS)) {
226
+ return {
227
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
228
+ verdict: 'human_required', reason_code: 'material_commitment',
229
+ reason: 'The requested decision creates a material financial, legal, or contractual commitment.',
230
+ question, source: 'deterministic', instruction_patch: null, resolver_decision: null
231
+ };
232
+ }
233
+ const destructive = containsAny(question, DESTRUCTIVE_TERMS);
234
+ const production = containsAny(question, PRODUCTION_ACTION_TERMS);
235
+ const authorityChange = containsAny(question, AUTHORITY_CHANGE_TERMS);
236
+ const sensitiveData = containsAny(question, SENSITIVE_DATA_TERMS);
237
+ const externalAction = containsAny(question, EXTERNAL_ACTION_TERMS);
238
+ const authorizedReleaseAction = requestAuthorizesExternalAction(context.request, question);
239
+ if (destructive || authorityChange || sensitiveData
240
+ || ((production || externalAction) && !authorizedReleaseAction)) {
241
+ return {
242
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
243
+ verdict: 'human_required', reason_code: 'irreversible_external_action',
244
+ reason: 'The requested decision crosses a production, destructive, authority, sensitive-data, or unapproved external-action boundary.',
245
+ question, source: 'deterministic', instruction_patch: null, resolver_decision: null
246
+ };
247
+ }
248
+ if (containsAny(question, EXPLICIT_NEW_OWNER_VALUE_TERMS)) {
249
+ return {
250
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
251
+ verdict: 'human_required', reason_code: 'owner_value_choice',
252
+ reason: 'The existing packet does not establish a reusable owner value choice.',
253
+ question, source: 'deterministic', instruction_patch: null, resolver_decision: null
254
+ };
255
+ }
256
+ if (containsAny(question, ROUTINE_TERMS)) {
257
+ return {
258
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
259
+ verdict: 'continue', reason_code: 'routine_reversible_work',
260
+ reason: 'The question concerns routine, reversible work already implied by the task.',
261
+ question, source: 'deterministic', instruction_patch: continuationPatch(question, false), resolver_decision: null
262
+ };
263
+ }
264
+ if (!resolver) {
265
+ return {
266
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
267
+ verdict: 'continue', reason_code: 'semantic_judgment_required',
268
+ reason: 'The case requires semantic application of Brainbase judgments; the current Codex worker must resolve it before escalating.',
269
+ question, source: 'same_codex', instruction_patch: continuationPatch(question, true), resolver_decision: null
270
+ };
271
+ }
272
+ const decision = validateResolverDecision(await resolver(resolverRequest(context, question, caseId)), caseId);
273
+ if (decision.verdict === 'human_required') {
274
+ return {
275
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
276
+ verdict: 'human_required', reason_code: 'resolver_human_required',
277
+ reason: decision.reason, question: decision.human_question ?? question,
278
+ source: 'independent_resolver', instruction_patch: null, resolver_decision: decision
279
+ };
280
+ }
281
+ return {
282
+ schema_version: 'brainbase-autonomy-evaluation-v1', case_id: caseId,
283
+ verdict: 'continue', reason_code: 'resolver_continue',
284
+ reason: decision.reason, question,
285
+ source: 'independent_resolver', instruction_patch: decision.instruction_patch ?? null,
286
+ resolver_decision: decision
287
+ };
288
+ }
289
+ export function renderJudgmentAutonomyContinuation(evaluation) {
290
+ if (evaluation.verdict !== 'continue' || !evaluation.instruction_patch) {
291
+ throw new TypeError('Only continue evaluations can be rendered as a continuation');
292
+ }
293
+ const patch = evaluation.instruction_patch;
294
+ return [
295
+ 'この確認質問は人間へ送らず、Brainbaseの代理判断ループとして同じターンを続行してください。',
296
+ `判定理由: ${evaluation.reason}`,
297
+ `中止: ${patch.cancel.join(' / ')}`,
298
+ `次に行うこと:\n${patch.do_next.map((item, index) => `${index + 1}. ${item}`).join('\n')}`,
299
+ `完了条件:\n${patch.acceptance_criteria.map((item) => `- ${item}`).join('\n')}`
300
+ ].join('\n');
301
+ }
@@ -0,0 +1,70 @@
1
+ import { type JudgmentDAG, type JudgmentDAGMetadata, type JudgmentDAGNode, type JudgmentDAGRunnerType } from './judgment-dag-core.js';
2
+ /** A JSON-compatible value accepted at every local runner boundary. */
3
+ export type JudgmentDAGJSONValue = JudgmentDAGMetadata;
4
+ export interface JudgmentDAGRunnerInput {
5
+ readonly run_id: string;
6
+ readonly dag: JudgmentDAG;
7
+ readonly node: JudgmentDAGNode;
8
+ readonly input: JudgmentDAGJSONValue;
9
+ readonly dependency_outputs: readonly JudgmentDAGDependencyOutput[];
10
+ }
11
+ export interface JudgmentDAGDependencyOutput {
12
+ readonly node_id: string;
13
+ readonly output: JudgmentDAGJSONValue;
14
+ }
15
+ export interface JudgmentDAGRunnerRegistration {
16
+ readonly version: string;
17
+ readonly run: (input: JudgmentDAGRunnerInput) => JudgmentDAGJSONValue | Promise<JudgmentDAGJSONValue>;
18
+ }
19
+ export interface JudgmentDAGRunRequest {
20
+ readonly run_id: string;
21
+ readonly dag: JudgmentDAG;
22
+ readonly input: JudgmentDAGJSONValue;
23
+ readonly runners: Partial<Record<JudgmentDAGRunnerType, JudgmentDAGRunnerRegistration>>;
24
+ }
25
+ export interface JudgmentDAGRunnerVersion {
26
+ readonly runner_type: JudgmentDAGRunnerType;
27
+ readonly version: string;
28
+ }
29
+ export interface JudgmentDAGNodeRunRecord {
30
+ readonly node_id: string;
31
+ readonly runner_type: JudgmentDAGRunnerType;
32
+ readonly runner_version: string;
33
+ readonly input_contract: string;
34
+ readonly output_contract: string;
35
+ readonly input: JudgmentDAGJSONValue;
36
+ readonly dependency_outputs: readonly JudgmentDAGDependencyOutput[];
37
+ readonly output: JudgmentDAGJSONValue;
38
+ }
39
+ export interface JudgmentDAGRunRecord {
40
+ readonly run_id: string;
41
+ readonly dag: JudgmentDAG;
42
+ readonly input: JudgmentDAGJSONValue;
43
+ readonly execution_order: readonly string[];
44
+ readonly runner_versions: readonly JudgmentDAGRunnerVersion[];
45
+ readonly nodes: readonly JudgmentDAGNodeRunRecord[];
46
+ }
47
+ export type JudgmentDAGExecutionCode = 'invalid_request' | 'missing_runner' | 'invalid_runner' | 'invalid_json' | 'runner_failed';
48
+ export type JudgmentDAGRunnerFailureKind = 'sync_throw' | 'async_reject';
49
+ export interface JudgmentDAGExecutionErrorDetails {
50
+ readonly node_id?: string;
51
+ readonly node_type?: JudgmentDAGNode['node_type'];
52
+ readonly runner_type?: JudgmentDAGRunnerType;
53
+ readonly failure_kind?: JudgmentDAGRunnerFailureKind;
54
+ readonly cause?: unknown;
55
+ }
56
+ export declare class JudgmentDAGExecutionError extends Error {
57
+ readonly code: JudgmentDAGExecutionCode;
58
+ readonly node_id?: string;
59
+ readonly node_type?: JudgmentDAGNode['node_type'];
60
+ readonly runner_type?: JudgmentDAGRunnerType;
61
+ readonly failure_kind?: JudgmentDAGRunnerFailureKind;
62
+ readonly cause?: unknown;
63
+ constructor(code: JudgmentDAGExecutionCode, message: string, details?: JudgmentDAGExecutionErrorDetails);
64
+ }
65
+ /**
66
+ * Execute a validated local DAG sequentially using only explicit runner
67
+ * registrations. No clock, randomness, filesystem, network, or persistence
68
+ * is consulted by this function.
69
+ */
70
+ export declare function executeJudgmentDAG(request: JudgmentDAGRunRequest): Promise<JudgmentDAGRunRecord>;