mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-23 20:15:19 +08:00
428 lines
13 KiB
JavaScript
428 lines
13 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { createHash } = require('node:crypto');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
BUILTIN_RUN_LOG_EXCERPT_TOOL,
|
|
BUILTIN_RUN_LOG_EXCERPT_TOOL_DEFINITION,
|
|
createBuiltInRunLogExcerptToolHandlerBinding,
|
|
} = require('@qinglong/runtime-core/builtin-run-log-excerpt-tool');
|
|
const {
|
|
createPluginPackageResourceGenerationFromReferences,
|
|
} = require('@qinglong/runtime-core/plugin-package-resource-generation');
|
|
const {
|
|
createProjectToolDefinitionSnapshot,
|
|
projectToolDefinitionRegistry,
|
|
} = require('@qinglong/runtime-core/project-tool-definition-snapshot');
|
|
const {
|
|
prepareToolInvocation,
|
|
} = require('@qinglong/runtime-core/tool-registry');
|
|
const {
|
|
TrustedToolHandlerBindingRegistry,
|
|
createTrustedToolInvocationPlan,
|
|
} = require('@qinglong/runtime-core/trusted-tool-invocation');
|
|
const {
|
|
COPILOT_FAILURE_DIAGNOSIS_ADMISSION_RECEIPT_SCHEMA,
|
|
COPILOT_FAILURE_DIAGNOSIS_EXECUTION_PLAN_SCHEMA,
|
|
InvalidCopilotFailureDiagnosisExecutionPlanError,
|
|
createCopilotFailureDiagnosisAdmissionBundle,
|
|
failureDiagnosisToolInputDigest,
|
|
normalizeCopilotFailureDiagnosisAdmissionReceipt,
|
|
normalizeCopilotFailureDiagnosisExecutionPlan,
|
|
prepareCopilotFailureDiagnosisExecution,
|
|
} = require('../dist/copilot/failure-diagnosis/executionAdmission.js');
|
|
const {
|
|
storedJsonEquals,
|
|
} = require('../dist/copilot/failure-diagnosis/admission/postgresRepository.js');
|
|
const {
|
|
COPILOT_FAILURE_DIAGNOSIS_TOOL_UNLOCK_RECEIPT_SCHEMA,
|
|
createCopilotFailureDiagnosisToolUnlockCommand,
|
|
normalizeCopilotFailureDiagnosisToolUnlockCommand,
|
|
restoreCopilotFailureDiagnosisTrustedToolAuthority,
|
|
} = require('../dist/copilot/failure-diagnosis/toolExecution.js');
|
|
|
|
const DIGEST_A = 'a'.repeat(64);
|
|
const DIGEST_B = 'b'.repeat(64);
|
|
const DIGEST_C = 'c'.repeat(64);
|
|
const SUBJECT = Object.freeze({ type: 'user', id: 'usr-diagnosis-owner' });
|
|
const FENCE = Object.freeze({ projectVersion: 3, bindingVersion: 7 });
|
|
const SOURCE = Object.freeze({
|
|
runId: 'run-source-failed',
|
|
runVersion: 8,
|
|
runStatus: 'failed',
|
|
attemptId: 'attempt-source-final',
|
|
attemptStatus: 'failed',
|
|
attemptFinishedAtMs: 1_900,
|
|
logArtifactId: 'artifact-source-log',
|
|
});
|
|
|
|
test('compares PostgreSQL jsonb evidence independent of object key order', () => {
|
|
assert.equal(
|
|
storedJsonEquals(
|
|
{ outer: { beta: 2, alpha: 1 }, items: [{ right: 2, left: 1 }] },
|
|
{ items: [{ left: 1, right: 2 }], outer: { alpha: 1, beta: 2 } },
|
|
),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
storedJsonEquals({ outer: { alpha: 1 } }, { outer: { alpha: 2 } }),
|
|
false,
|
|
);
|
|
});
|
|
|
|
function snapshot() {
|
|
const generation = createPluginPackageResourceGenerationFromReferences({
|
|
installationId: 'install-qinglong-run-log-excerpt',
|
|
projectId: 'project-diagnosis',
|
|
packageName: 'qinglong',
|
|
lockDigest: DIGEST_A,
|
|
generation: 1,
|
|
previousActiveLockDigest: null,
|
|
contentDigest: DIGEST_B,
|
|
resources: [],
|
|
});
|
|
return createProjectToolDefinitionSnapshot({
|
|
projectId: 'project-diagnosis',
|
|
contributions: [
|
|
{
|
|
generation,
|
|
revisionDigest: DIGEST_C,
|
|
definitions: [BUILTIN_RUN_LOG_EXCERPT_TOOL_DEFINITION],
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
function principal() {
|
|
return {
|
|
subject: SUBJECT,
|
|
authenticationId: 'auth-diagnosis-owner',
|
|
authenticatedAtMs: 1_000,
|
|
expiresAtMs: 10_000,
|
|
assurance: 'multi_factor',
|
|
};
|
|
}
|
|
|
|
function authorizer(effect = 'allow') {
|
|
return {
|
|
async authorize() {
|
|
return {
|
|
effect,
|
|
reasons:
|
|
effect === 'allow'
|
|
? ['role_grant']
|
|
: ['agent_action_requires_approval'],
|
|
fence: FENCE,
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
async function toolHarness(options = {}) {
|
|
const currentSnapshot = snapshot();
|
|
const binding = createBuiltInRunLogExcerptToolHandlerBinding(
|
|
currentSnapshot,
|
|
[options.profile ?? 'cluster-control'],
|
|
);
|
|
const bindings = new TrustedToolHandlerBindingRegistry(currentSnapshot, [
|
|
binding,
|
|
]);
|
|
const source = options.source ?? SOURCE;
|
|
const invocation = await prepareToolInvocation(
|
|
projectToolDefinitionRegistry(currentSnapshot),
|
|
{
|
|
projectId: 'project-diagnosis',
|
|
principal: principal(),
|
|
nowMs: 1_200,
|
|
tool: BUILTIN_RUN_LOG_EXCERPT_TOOL,
|
|
input: {
|
|
runId: options.inputRunId ?? source.runId,
|
|
attemptId: options.inputAttemptId ?? source.attemptId,
|
|
},
|
|
},
|
|
authorizer(options.effect),
|
|
);
|
|
const bundle = createTrustedToolInvocationPlan(bindings, invocation, {
|
|
actionRef: 'diagnosis-log-tool-plan',
|
|
inputArtifactId: 'diagnosis-tool-input',
|
|
previewArtifactId: 'diagnosis-tool-preview',
|
|
artifactKeyId: 'diagnosis-input-key',
|
|
artifactKey: Buffer.alloc(32, 0x21),
|
|
artifactNonce: Buffer.alloc(12, 0x31),
|
|
profile: options.profile ?? 'cluster-control',
|
|
preview: {
|
|
title: 'Read failed Run log',
|
|
summary: 'Reads one bounded redacted log excerpt',
|
|
fields: [
|
|
{ kind: 'identifier', label: 'Run', value: source.runId },
|
|
{ kind: 'identifier', label: 'Attempt', value: source.attemptId },
|
|
],
|
|
warnings: ['potentially_sensitive_output'],
|
|
},
|
|
sealedAtMs: 2_000,
|
|
});
|
|
return { bindings, toolPlan: bundle.plan, source };
|
|
}
|
|
|
|
function model(overrides = {}) {
|
|
return {
|
|
provider: 'provider-primary',
|
|
model: 'model-diagnosis',
|
|
modelBoundary: 'external',
|
|
responseLanguage: 'zh-CN',
|
|
maxOutputTokens: 512,
|
|
egressPolicy: {
|
|
schema: 'qinglong/copilot-model-egress-policy@v1',
|
|
revision: 'policy-diagnosis-v1',
|
|
potentiallySensitiveDataBoundaries: ['on_device', 'external'],
|
|
maxInputBytes: 64 * 1024,
|
|
maxOutputTokens: 1_024,
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
async function plan(options = {}) {
|
|
const harness = await toolHarness(options);
|
|
return prepareCopilotFailureDiagnosisExecution({
|
|
requestId: 'request-diagnosis-001',
|
|
traceId: 'trace-diagnosis-001',
|
|
source: harness.source,
|
|
toolPlan: harness.toolPlan,
|
|
bindings: harness.bindings,
|
|
model: model(options.model),
|
|
deadlineAtMs: 8_000,
|
|
plannedAtMs: 2_100,
|
|
});
|
|
}
|
|
|
|
function successfulToolCompletion(current) {
|
|
const unsigned = Object.freeze({
|
|
schema: 'qinglong/tool-execution-completion@v1',
|
|
startId: 'cds:diagnosis-tool-start',
|
|
projectId: current.projectId,
|
|
runId: current.runId,
|
|
stepRunId: current.toolStepRunId,
|
|
startedStepRunVersion: 2,
|
|
completedStepRunVersion: 3,
|
|
barrierDigest: DIGEST_A,
|
|
adapterDigest: DIGEST_B,
|
|
resultArtifact: Object.freeze({
|
|
artifactId: 'cdra:diagnosis-result',
|
|
artifactDigest: DIGEST_C,
|
|
outputDigest: DIGEST_A,
|
|
executionResultDigest: DIGEST_B,
|
|
}),
|
|
stepRunMutationId: 'cdscm:diagnosis-tool',
|
|
stepRunMutationDigest: DIGEST_C,
|
|
completedStepRunDigest: DIGEST_A,
|
|
runEventId: 'cdsce:diagnosis-tool',
|
|
completedAtMs: 3_000,
|
|
});
|
|
return Object.freeze({
|
|
...unsigned,
|
|
completionDigest: createHash('sha256')
|
|
.update(
|
|
Buffer.from('qinglong/tool-execution-completion-digest@v1\0', 'utf8'),
|
|
)
|
|
.update(JSON.stringify(unsigned))
|
|
.digest('hex'),
|
|
});
|
|
}
|
|
|
|
test('creates one independent diagnosis Run with ready Tool and pending Model Steps', async () => {
|
|
const current = await plan();
|
|
assert.equal(current.schema, COPILOT_FAILURE_DIAGNOSIS_EXECUTION_PLAN_SCHEMA);
|
|
assert.equal(current.runId.startsWith('cdr:'), true);
|
|
assert.equal(current.toolStepRunId.startsWith('cdt:'), true);
|
|
assert.equal(current.modelStepRunId.startsWith('cdm:'), true);
|
|
assert.equal(current.modelInvocationId.startsWith('cdi:'), true);
|
|
assert.equal(
|
|
current.tool.invocationArtifact.inputDigest,
|
|
failureDiagnosisToolInputDigest(SOURCE),
|
|
);
|
|
assert.equal(current.model.egressPolicyDigest.length, 64);
|
|
assert.equal(current.model.intentDigest.length, 64);
|
|
|
|
const bundle = createCopilotFailureDiagnosisAdmissionBundle(current);
|
|
assert.equal(bundle.run.parentRunId, SOURCE.runId);
|
|
assert.equal(bundle.run.status, 'running');
|
|
assert.equal(bundle.run.version, 3);
|
|
assert.equal(bundle.run.eventSequence, 3);
|
|
assert.equal(bundle.admissionEvent.sequence, 1);
|
|
for (const event of [
|
|
bundle.admissionEvent,
|
|
bundle.toolStepMutation.event,
|
|
bundle.modelStepMutation.event,
|
|
]) {
|
|
assert.equal(Buffer.byteLength(event.id, 'utf8') <= 36, true);
|
|
}
|
|
assert.deepEqual(
|
|
{
|
|
kind: bundle.toolStepMutation.stepRun.kind,
|
|
status: bundle.toolStepMutation.stepRun.status,
|
|
sequence: bundle.toolStepMutation.event.sequence,
|
|
},
|
|
{ kind: 'tool', status: 'ready', sequence: 2 },
|
|
);
|
|
assert.equal(
|
|
bundle.toolStepMutation.stepRun.definitionRef,
|
|
'tool:qinglong.run.log.excerpt@1.0.0',
|
|
);
|
|
assert.deepEqual(
|
|
{
|
|
kind: bundle.modelStepMutation.stepRun.kind,
|
|
status: bundle.modelStepMutation.stepRun.status,
|
|
parentStepRunId: bundle.modelStepMutation.stepRun.parentStepRunId,
|
|
sequence: bundle.modelStepMutation.event.sequence,
|
|
},
|
|
{
|
|
kind: 'model',
|
|
status: 'pending',
|
|
parentStepRunId: current.toolStepRunId,
|
|
sequence: 3,
|
|
},
|
|
);
|
|
assert.equal(
|
|
bundle.receipt.schema,
|
|
COPILOT_FAILURE_DIAGNOSIS_ADMISSION_RECEIPT_SCHEMA,
|
|
);
|
|
assert.equal(bundle.receipt.finalRunVersion, 3);
|
|
assert.deepEqual(
|
|
normalizeCopilotFailureDiagnosisExecutionPlan(
|
|
JSON.parse(JSON.stringify(current)),
|
|
),
|
|
current,
|
|
);
|
|
assert.deepEqual(
|
|
normalizeCopilotFailureDiagnosisAdmissionReceipt(
|
|
JSON.parse(JSON.stringify(bundle.receipt)),
|
|
),
|
|
bundle.receipt,
|
|
);
|
|
});
|
|
|
|
test('restores the exact admitted Tool and binds success to one Model unlock mutation', async () => {
|
|
const current = await plan();
|
|
const admission = createCopilotFailureDiagnosisAdmissionBundle(current);
|
|
const authority = restoreCopilotFailureDiagnosisTrustedToolAuthority(
|
|
current,
|
|
snapshot(),
|
|
);
|
|
assert.equal(authority.plan.planDigest, current.tool.planDigest);
|
|
assert.equal(authority.binding.bindingDigest, current.tool.bindingDigest);
|
|
|
|
const command = createCopilotFailureDiagnosisToolUnlockCommand({
|
|
plan: current,
|
|
completion: successfulToolCompletion(current),
|
|
modelStepRun: admission.modelStepMutation.stepRun,
|
|
run: {
|
|
id: current.runId,
|
|
projectId: current.projectId,
|
|
status: 'running',
|
|
version: 5,
|
|
eventSequence: 5,
|
|
},
|
|
});
|
|
assert.equal(
|
|
command.receipt.schema,
|
|
COPILOT_FAILURE_DIAGNOSIS_TOOL_UNLOCK_RECEIPT_SCHEMA,
|
|
);
|
|
assert.equal(command.modelStepRunMutation.previousStatus, 'pending');
|
|
assert.equal(command.modelStepRunMutation.stepRun.status, 'ready');
|
|
assert.equal(command.receipt.finalRunVersion, 6);
|
|
assert.deepEqual(
|
|
normalizeCopilotFailureDiagnosisToolUnlockCommand(
|
|
JSON.parse(JSON.stringify(command)),
|
|
),
|
|
command,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
normalizeCopilotFailureDiagnosisToolUnlockCommand({
|
|
...command,
|
|
receipt: { ...command.receipt, finalRunVersion: 7 },
|
|
}),
|
|
TypeError,
|
|
);
|
|
});
|
|
|
|
test('publishes Tool execution only through explicit AI subpaths', () => {
|
|
const root = require('../dist');
|
|
const execution = require('@qinglong/ai/failure-diagnosis-tool-execution');
|
|
const storage = require('@qinglong/ai/postgres-failure-diagnosis-tool-execution-storage');
|
|
assert.equal(root.executeCopilotFailureDiagnosisTool, undefined);
|
|
assert.equal(typeof execution.executeCopilotFailureDiagnosisTool, 'function');
|
|
assert.equal(
|
|
typeof storage.PostgresCopilotFailureDiagnosisToolUnlockRepository,
|
|
'function',
|
|
);
|
|
});
|
|
|
|
test('rejects a Tool input detached from the source failure fence', async () => {
|
|
await assert.rejects(
|
|
() => plan({ inputAttemptId: 'attempt-unrelated' }),
|
|
InvalidCopilotFailureDiagnosisExecutionPlanError,
|
|
);
|
|
});
|
|
|
|
test('rejects approval-pending or non-Cluster Tool authority before Run creation', async () => {
|
|
await assert.rejects(
|
|
() => plan({ effect: 'require_approval' }),
|
|
InvalidCopilotFailureDiagnosisExecutionPlanError,
|
|
);
|
|
await assert.rejects(
|
|
() => plan({ profile: 'edge' }),
|
|
InvalidCopilotFailureDiagnosisExecutionPlanError,
|
|
);
|
|
});
|
|
|
|
test('rejects inconsistent source terminal evidence and denied model egress', async () => {
|
|
await assert.rejects(
|
|
() =>
|
|
plan({
|
|
source: {
|
|
...SOURCE,
|
|
runStatus: 'timed_out',
|
|
attemptStatus: 'failed',
|
|
},
|
|
}),
|
|
InvalidCopilotFailureDiagnosisExecutionPlanError,
|
|
);
|
|
await assert.rejects(
|
|
() =>
|
|
plan({
|
|
model: {
|
|
egressPolicy: {
|
|
...model().egressPolicy,
|
|
potentiallySensitiveDataBoundaries: ['on_device'],
|
|
},
|
|
},
|
|
}),
|
|
InvalidCopilotFailureDiagnosisExecutionPlanError,
|
|
);
|
|
});
|
|
|
|
test('fails closed on widened or digest-drifted durable plans', async () => {
|
|
const current = await plan();
|
|
assert.throws(
|
|
() =>
|
|
normalizeCopilotFailureDiagnosisExecutionPlan({
|
|
...current,
|
|
callerAuthority: 'execute',
|
|
}),
|
|
InvalidCopilotFailureDiagnosisExecutionPlanError,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
normalizeCopilotFailureDiagnosisExecutionPlan({
|
|
...current,
|
|
source: {
|
|
...current.source,
|
|
runVersion: current.source.runVersion + 1,
|
|
},
|
|
}),
|
|
InvalidCopilotFailureDiagnosisExecutionPlanError,
|
|
);
|
|
});
|