Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/copilot_issue_comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ jobs:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
CURSOR_API_KEY: ${{ (vars.AGENT_PROVIDER == 'cursor' || vars.FINDINGS_PROVIDER == 'cursor' || vars.FIXER_PROVIDER == 'cursor' || vars.PLANNER_PROVIDER == 'cursor' || vars.REVIEWER_PROVIDER == 'cursor' || vars.TESTER_PROVIDER == 'cursor') && secrets.CURSOR_API_KEY || '' }}
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
2 changes: 1 addition & 1 deletion .github/workflows/copilot_pull_request_comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ jobs:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
CURSOR_API_KEY: ${{ (vars.AGENT_PROVIDER == 'cursor' || vars.FINDINGS_PROVIDER == 'cursor' || vars.FIXER_PROVIDER == 'cursor' || vars.PLANNER_PROVIDER == 'cursor' || vars.REVIEWER_PROVIDER == 'cursor' || vars.TESTER_PROVIDER == 'cursor') && secrets.CURSOR_API_KEY || '' }}
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
113 changes: 110 additions & 3 deletions build/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93563,6 +93563,8 @@ function validatePaths(source, inventory, sddPath) {
if (change === 'create') {
if (inventory.files.has(file) || inventory.directories.has(file))
fail('repository-path');
if ([...proposed].some(other => file.startsWith(`${other}/`) || other.startsWith(`${file}/`)))
fail('repository-path');
const segments = file.split('/');
if (segments.some((_, index) => inventory.files.has(segments.slice(0, index + 1).join('/')) || inventory.blocked.has(segments.slice(0, index + 1).join('/'))))
fail('repository-path');
Expand Down Expand Up @@ -104055,6 +104057,7 @@ async function prepareRepositoryVerification(setup, environment) {
const session = process.env.COPILOT_NATIVE_WORKER_SESSION;
if (!session)
throw new verification_setup_policy_1.VerificationSetupError('native-workflow-required', setup.sourceSha);
let requestCreated = false;
try {
(0, native_verification_protocol_1.nativeSession)(session);
const requests = (0, native_verification_protocol_1.readNativeRecord)(session, 'request-count.json')?.count ?? 0;
Expand All @@ -104068,6 +104071,7 @@ async function prepareRepositoryVerification(setup, environment) {
(0, native_verification_staging_1.stageNativeVerificationAction)(setup, session);
const id = (0, node_crypto_1.randomUUID)();
(0, native_verification_protocol_1.writeNativeRecord)(session, 'request.json', { id, sourceSha: setup.sourceSha, digest: (0, verification_setup_snapshot_1.verificationSetupDigest)(setup), environment });
requestCreated = true;
(0, logging_ports_1.logInfo)(`Preparing repository verification (${verification_setup_policy_1.VERIFICATION_SETUP_PATH}; trusted source SHA: ${setup.sourceSha}).`);
const deadline = Date.now() + verification_setup_policy_1.VERIFICATION_SETUP_LIMITS.timeoutMs;
let response;
Expand Down Expand Up @@ -104101,7 +104105,8 @@ async function prepareRepositoryVerification(setup, environment) {
throw new verification_setup_policy_1.VerificationSetupError(error instanceof verification_setup_policy_1.VerificationSetupError ? error.reason : 'handoff-invalid', setup.sourceSha);
}
finally {
(0, node_fs_1.rmSync)((0, node_path_1.join)(session, 'request.json'), { force: true });
if (requestCreated)
(0, node_fs_1.rmSync)((0, node_path_1.join)(session, 'request.json'), { force: true });
}
}

Expand Down Expand Up @@ -104371,9 +104376,22 @@ const logger_1 = __nccwpck_require__(91151);
const issue_content_interface_1 = __nccwpck_require__(60608);
const configuration_payload_policy_1 = __nccwpck_require__(58043);
const application_error_1 = __nccwpck_require__(75999);
const configuration_markers_1 = __nccwpck_require__(4586);
class ConfigurationHandler extends issue_content_interface_1.IssueContentInterface {
constructor() {
super(...arguments);
this.getContent = (description) => {
const block = (0, configuration_markers_1.readConfigurationBlock)(description);
return block.kind === 'present' ? block.content : undefined;
};
this.updateContent = (description, content) => {
if (description === undefined || content === undefined)
return undefined;
const updated = (0, configuration_markers_1.writeConfigurationBlock)(description, content);
if (updated === undefined)
(0, logger_1.logError)('The content has a problem with open-close tags: stored configuration.');
return updated;
};
this.update = async (query, context) => {
const storedRaw = await this.internalGetter(query);
return this.internalUpdate(query, (0, configuration_payload_policy_1.buildConfigurationPayload)(context, storedRaw));
Expand All @@ -104393,8 +104411,7 @@ class ConfigurationHandler extends issue_content_interface_1.IssueContentInterfa
try {
const config = this.getContent(description);
if (config === undefined) {
if (description?.includes('<!-- copilot-configuration-start')
|| description?.includes('copilot-configuration-end -->')) {
if ((0, configuration_markers_1.readConfigurationBlock)(description).kind === 'invalid') {
throw new Error('Stored configuration markers are inconsistent.');
}
return undefined;
Expand Down Expand Up @@ -104430,6 +104447,96 @@ class ConfigurationHandler extends issue_content_interface_1.IssueContentInterfa
exports.ConfigurationHandler = ConfigurationHandler;


/***/ }),

/***/ 4586:
/***/ ((__unused_webpack_module, exports) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CONFIGURATION_END = exports.CONFIGURATION_START = void 0;
exports.readConfigurationBlock = readConfigurationBlock;
exports.writeConfigurationBlock = writeConfigurationBlock;
exports.CONFIGURATION_START = '<!-- copilot-configuration-start';
exports.CONFIGURATION_END = 'copilot-configuration-end -->';
/** Owned delimiters begin a line; ordinary mentions are not state. Complete legacy fenced records remain readable. */
function readConfigurationBlock(description) {
if (description === undefined)
return { kind: 'absent' };
let fence;
let fencedMarker = false;
let fencedStart;
let nestedFencedStart = false;
let start;
let contentStart = 0;
let block;
let offset = 0;
for (const line of description.match(/[^\n]*(?:\n|$)/gu) ?? []) {
const text = line.replace(/\r?\n$/u, '');
const starts = /^ {0,3}<!-- copilot-configuration-start(?:[ \t]|$)/u.test(text);
const ends = /^ {0,3}copilot-configuration-end -->[ \t]*$/u.test(text);
const boundary = /^ {0,3}(`{3,}|~{3,})(.*)$/u.exec(text);
if (fence) {
fencedMarker || (fencedMarker = starts || ends);
if (starts) {
if (fencedStart !== undefined)
nestedFencedStart = true;
else
fencedStart = offset + text.indexOf(exports.CONFIGURATION_START);
}
const end = text.indexOf(exports.CONFIGURATION_END);
if (fencedStart !== undefined && end >= 0 && !text.slice(end + exports.CONFIGURATION_END.length).trim()) {
if (block || nestedFencedStart)
return { kind: 'invalid' };
block = { kind: 'present', start: fencedStart, end: offset + end + exports.CONFIGURATION_END.length,
content: description.slice(fencedStart + exports.CONFIGURATION_START.length, offset + end) };
fencedStart = undefined;
}
if (boundary && boundary[1][0] === fence[0] && boundary[1].length >= fence.length && !boundary[2].trim()) {
fence = undefined;
fencedMarker = false;
fencedStart = undefined;
nestedFencedStart = false;
}
}
else if (start !== undefined || starts) {
if (starts) {
if (start !== undefined || block)
return { kind: 'invalid' };
start = offset + text.indexOf(exports.CONFIGURATION_START);
contentStart = start + exports.CONFIGURATION_START.length;
}
const end = text.indexOf(exports.CONFIGURATION_END);
if (end >= 0 && !text.slice(end + exports.CONFIGURATION_END.length).trim()) {
block = { kind: 'present', start: start, end: offset + end + exports.CONFIGURATION_END.length,
content: description.slice(contentStart, offset + end) };
start = undefined;
}
}
else if (ends)
return { kind: 'invalid' };
else if (boundary && (boundary[1][0] !== '`' || !boundary[2].includes('`')))
fence = boundary[1];
offset += line.length;
}
// A legacy block behind an unclosed fence is ambiguous: never silently discard its state.
if (start !== undefined || fence && fencedMarker)
return { kind: 'invalid' };
return block ?? { kind: 'absent', ...(fence ? { closingFence: fence } : {}) };
}
function writeConfigurationBlock(description, content) {
const block = readConfigurationBlock(description);
if (block.kind === 'invalid')
return undefined;
const replacement = `${exports.CONFIGURATION_START}\n${content}\n${exports.CONFIGURATION_END}`;
if (block.kind === 'present')
return description.slice(0, block.start) + replacement + description.slice(block.end);
// Closing an otherwise empty dangling example preserves its rendered text and keeps new state outside it.
return `${description}\n\n${block.closingFence ? `${block.closingFence}\n\n` : ''}${replacement}`;
}


/***/ }),

/***/ 58043:
Expand Down
113 changes: 110 additions & 3 deletions build/github_action/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84007,6 +84007,8 @@ function validatePaths(source, inventory, sddPath) {
if (change === 'create') {
if (inventory.files.has(file) || inventory.directories.has(file))
fail('repository-path');
if ([...proposed].some(other => file.startsWith(`${other}/`) || other.startsWith(`${file}/`)))
fail('repository-path');
const segments = file.split('/');
if (segments.some((_, index) => inventory.files.has(segments.slice(0, index + 1).join('/')) || inventory.blocked.has(segments.slice(0, index + 1).join('/'))))
fail('repository-path');
Expand Down Expand Up @@ -90617,6 +90619,7 @@ async function prepareRepositoryVerification(setup, environment) {
const session = process.env.COPILOT_NATIVE_WORKER_SESSION;
if (!session)
throw new verification_setup_policy_1.VerificationSetupError('native-workflow-required', setup.sourceSha);
let requestCreated = false;
try {
(0, native_verification_protocol_1.nativeSession)(session);
const requests = (0, native_verification_protocol_1.readNativeRecord)(session, 'request-count.json')?.count ?? 0;
Expand All @@ -90630,6 +90633,7 @@ async function prepareRepositoryVerification(setup, environment) {
(0, native_verification_staging_1.stageNativeVerificationAction)(setup, session);
const id = (0, node_crypto_1.randomUUID)();
(0, native_verification_protocol_1.writeNativeRecord)(session, 'request.json', { id, sourceSha: setup.sourceSha, digest: (0, verification_setup_snapshot_1.verificationSetupDigest)(setup), environment });
requestCreated = true;
(0, logging_ports_1.logInfo)(`Preparing repository verification (${verification_setup_policy_1.VERIFICATION_SETUP_PATH}; trusted source SHA: ${setup.sourceSha}).`);
const deadline = Date.now() + verification_setup_policy_1.VERIFICATION_SETUP_LIMITS.timeoutMs;
let response;
Expand Down Expand Up @@ -90663,7 +90667,8 @@ async function prepareRepositoryVerification(setup, environment) {
throw new verification_setup_policy_1.VerificationSetupError(error instanceof verification_setup_policy_1.VerificationSetupError ? error.reason : 'handoff-invalid', setup.sourceSha);
}
finally {
(0, node_fs_1.rmSync)((0, node_path_1.join)(session, 'request.json'), { force: true });
if (requestCreated)
(0, node_fs_1.rmSync)((0, node_path_1.join)(session, 'request.json'), { force: true });
}
}

Expand Down Expand Up @@ -90933,9 +90938,22 @@ const logger_1 = __nccwpck_require__(91151);
const issue_content_interface_1 = __nccwpck_require__(60608);
const configuration_payload_policy_1 = __nccwpck_require__(58043);
const application_error_1 = __nccwpck_require__(75999);
const configuration_markers_1 = __nccwpck_require__(4586);
class ConfigurationHandler extends issue_content_interface_1.IssueContentInterface {
constructor() {
super(...arguments);
this.getContent = (description) => {
const block = (0, configuration_markers_1.readConfigurationBlock)(description);
return block.kind === 'present' ? block.content : undefined;
};
this.updateContent = (description, content) => {
if (description === undefined || content === undefined)
return undefined;
const updated = (0, configuration_markers_1.writeConfigurationBlock)(description, content);
if (updated === undefined)
(0, logger_1.logError)('The content has a problem with open-close tags: stored configuration.');
return updated;
};
this.update = async (query, context) => {
const storedRaw = await this.internalGetter(query);
return this.internalUpdate(query, (0, configuration_payload_policy_1.buildConfigurationPayload)(context, storedRaw));
Expand All @@ -90955,8 +90973,7 @@ class ConfigurationHandler extends issue_content_interface_1.IssueContentInterfa
try {
const config = this.getContent(description);
if (config === undefined) {
if (description?.includes('<!-- copilot-configuration-start')
|| description?.includes('copilot-configuration-end -->')) {
if ((0, configuration_markers_1.readConfigurationBlock)(description).kind === 'invalid') {
throw new Error('Stored configuration markers are inconsistent.');
}
return undefined;
Expand Down Expand Up @@ -90992,6 +91009,96 @@ class ConfigurationHandler extends issue_content_interface_1.IssueContentInterfa
exports.ConfigurationHandler = ConfigurationHandler;


/***/ }),

/***/ 4586:
/***/ ((__unused_webpack_module, exports) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CONFIGURATION_END = exports.CONFIGURATION_START = void 0;
exports.readConfigurationBlock = readConfigurationBlock;
exports.writeConfigurationBlock = writeConfigurationBlock;
exports.CONFIGURATION_START = '<!-- copilot-configuration-start';
exports.CONFIGURATION_END = 'copilot-configuration-end -->';
/** Owned delimiters begin a line; ordinary mentions are not state. Complete legacy fenced records remain readable. */
function readConfigurationBlock(description) {
if (description === undefined)
return { kind: 'absent' };
let fence;
let fencedMarker = false;
let fencedStart;
let nestedFencedStart = false;
let start;
let contentStart = 0;
let block;
let offset = 0;
for (const line of description.match(/[^\n]*(?:\n|$)/gu) ?? []) {
const text = line.replace(/\r?\n$/u, '');
const starts = /^ {0,3}<!-- copilot-configuration-start(?:[ \t]|$)/u.test(text);
const ends = /^ {0,3}copilot-configuration-end -->[ \t]*$/u.test(text);
const boundary = /^ {0,3}(`{3,}|~{3,})(.*)$/u.exec(text);
if (fence) {
fencedMarker || (fencedMarker = starts || ends);
if (starts) {
if (fencedStart !== undefined)
nestedFencedStart = true;
else
fencedStart = offset + text.indexOf(exports.CONFIGURATION_START);
}
const end = text.indexOf(exports.CONFIGURATION_END);
if (fencedStart !== undefined && end >= 0 && !text.slice(end + exports.CONFIGURATION_END.length).trim()) {
if (block || nestedFencedStart)
return { kind: 'invalid' };
block = { kind: 'present', start: fencedStart, end: offset + end + exports.CONFIGURATION_END.length,
content: description.slice(fencedStart + exports.CONFIGURATION_START.length, offset + end) };
fencedStart = undefined;
}
if (boundary && boundary[1][0] === fence[0] && boundary[1].length >= fence.length && !boundary[2].trim()) {
fence = undefined;
fencedMarker = false;
fencedStart = undefined;
nestedFencedStart = false;
}
}
else if (start !== undefined || starts) {
if (starts) {
if (start !== undefined || block)
return { kind: 'invalid' };
start = offset + text.indexOf(exports.CONFIGURATION_START);
contentStart = start + exports.CONFIGURATION_START.length;
}
const end = text.indexOf(exports.CONFIGURATION_END);
if (end >= 0 && !text.slice(end + exports.CONFIGURATION_END.length).trim()) {
block = { kind: 'present', start: start, end: offset + end + exports.CONFIGURATION_END.length,
content: description.slice(contentStart, offset + end) };
start = undefined;
}
}
else if (ends)
return { kind: 'invalid' };
else if (boundary && (boundary[1][0] !== '`' || !boundary[2].includes('`')))
fence = boundary[1];
offset += line.length;
}
// A legacy block behind an unclosed fence is ambiguous: never silently discard its state.
if (start !== undefined || fence && fencedMarker)
return { kind: 'invalid' };
return block ?? { kind: 'absent', ...(fence ? { closingFence: fence } : {}) };
}
function writeConfigurationBlock(description, content) {
const block = readConfigurationBlock(description);
if (block.kind === 'invalid')
return undefined;
const replacement = `${exports.CONFIGURATION_START}\n${content}\n${exports.CONFIGURATION_END}`;
if (block.kind === 'present')
return description.slice(0, block.start) + replacement + description.slice(block.end);
// Closing an otherwise empty dangling example preserves its rendered text and keeps new state outside it.
return `${description}\n\n${block.closingFence ? `${block.closingFence}\n\n` : ''}${replacement}`;
}


/***/ }),

/***/ 58043:
Expand Down
3 changes: 3 additions & 0 deletions docs/issues/pre-branch-sdds.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ choose distinct full IDs, reconcile each area's cases, repair both directions
of the mappings, or correct the paths and explicitly proposed files. No further
generation or publication occurs in that run. Review the named contract and
retry the same issue workflow; answered questions and historical commits remain.
Proposed files must also be compatible with one another: a file cannot be the
parent directory of another proposed file, in either declaration order or at
any nesting depth. Sibling files and distinct pathname prefixes remain valid.
All validation and freshness checks still apply. If the second draft still fails,
no draft is published; existing answers and SDDs are retained. Retry with a proportional
change or an approved companion specification.
Expand Down
Loading
Loading