@amistio/cli 0.1.13 → 0.1.15

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/README.md CHANGED
@@ -41,7 +41,7 @@ When `--tool copilot` uses the GitHub Copilot SDK, Amistio approves read-only pe
41
41
 
42
42
  `amistio runner status` reports local background runner state, latest heartbeat, and bounded resource usage when available. Resource usage is latest-sample runner process memory/CPU plus safe aggregate system memory/load signals; it does not include source files, environment variables, command lines, process lists, credentials, or arbitrary local paths.
43
43
 
44
- The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only `projectContextRefresh` jobs from the workspace Context panel and create due runner-driven refreshes when no fresh approved map exists. Context refreshes inspect the paired checkout locally without modifying files and submit only bounded summaries, slices, entities, relations, safe citations, confidence, freshness, and repo-relative paths. Approved maps are reused as context packs for source-aware assistant and impact-preview work. Current runners can also claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can claim manual read-only `appEvaluationScan` jobs from the workspace Evaluate panel and create at most one due hourly evaluation during normal watch/background polling when app evaluation is enabled for the repository link. Evaluation results contain bounded summaries, safe evidence, suggested actions, lifecycle proposals, and repo-relative paths only. Current runners can also claim manual read-only `securityPostureScan` jobs from the workspace Security panel and create due daily posture checks during normal watch/background polling. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only; source, secrets, environment variables, command lines, process lists, credentials, provider sessions, and arbitrary local paths stay local. Implementation or cleanup is queued separately only after the user approves an issue analysis, app evaluation finding, or security remediation plan in the app.
44
+ The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only `projectContextRefresh` jobs from the workspace Context panel and create due runner-driven refreshes when no fresh approved map exists. Context refreshes inspect the paired checkout locally without modifying files and submit only bounded summaries, slices, entities, relations, safe citations, confidence, freshness, and repo-relative paths. If a submitted context refresh contains unsafe evidence, unsafe paths, or a map too large to store safely, Amistio marks the refresh failed with a safe reason instead of storing the rejected raw result. Approved maps are reused as context packs for source-aware assistant and impact-preview work. Current runners can also claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can claim manual read-only `appEvaluationScan` jobs from the workspace Evaluate panel and create at most one due hourly evaluation during normal watch/background polling when app evaluation is enabled for the repository link. Evaluation results contain bounded summaries, safe evidence, suggested actions, lifecycle proposals, and repo-relative paths only. Current runners can also claim manual read-only `securityPostureScan` jobs from the workspace Security panel and create due daily posture checks during normal watch/background polling. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only; source, secrets, environment variables, command lines, process lists, credentials, provider sessions, and arbitrary local paths stay local. Implementation or cleanup is queued separately only after the user approves an issue analysis, app evaluation finding, or security remediation plan in the app.
45
45
 
46
46
  Approved implementation work uses Git as the handoff boundary. After the local tool completes successfully, the runner commits the isolated worktree, pushes an `amistio/work/...` branch to the linked GitHub remote, opens or reuses a pull request with the locally authenticated `gh` CLI, reports only safe PR metadata to Amistio, and removes the local worktree after the PR URL is durable. Prepare the runner machine with Git commit identity, push permission to the linked remote, and `gh auth status`. If commit, push, or PR creation fails, the work item is blocked and the branch/worktree stay on disk for manual recovery; source files and patches are not uploaded to Amistio.
47
47
 
package/dist/index.js CHANGED
@@ -1099,11 +1099,26 @@ var securityFindingResultSchema = z.object({
1099
1099
  safePaths: z.array(z.string().trim().min(1).max(300)).default([]),
1100
1100
  status: securityFindingStatusSchema.default("open")
1101
1101
  });
1102
+ var securityPostureGradeSchema = z.enum(["A", "B", "C", "D", "F", "Unknown"]);
1103
+ var securityPostureScanResultGradeSchema = z.preprocess((value) => {
1104
+ if (typeof value !== "string") {
1105
+ return value;
1106
+ }
1107
+ const trimmed = value.trim();
1108
+ if (trimmed.toUpperCase() === "UNKNOWN") {
1109
+ return "Unknown";
1110
+ }
1111
+ const gradeMatch = /^(?:GRADE\s*)?([ABCDF])(?:\s*[+-])?(?:\b|$)/i.exec(trimmed);
1112
+ if (gradeMatch?.[1]) {
1113
+ return gradeMatch[1].toUpperCase();
1114
+ }
1115
+ return "Unknown";
1116
+ }, securityPostureGradeSchema);
1102
1117
  var securityPostureScanResultSchema = z.object({
1103
1118
  summary: z.string().trim().min(1).max(2e3),
1104
1119
  baselineVersion: z.string().trim().min(1).max(80),
1105
1120
  postureScore: z.number().min(0).max(100).optional(),
1106
- postureGrade: z.enum(["A", "B", "C", "D", "F", "Unknown"]).default("Unknown"),
1121
+ postureGrade: securityPostureScanResultGradeSchema.default("Unknown"),
1107
1122
  categorySummaries: z.array(securityCategorySummarySchema).default([]),
1108
1123
  findings: z.array(securityFindingResultSchema).default([]),
1109
1124
  verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1)
@@ -1162,7 +1177,7 @@ var securityPostureSnapshotItemSchema = baseItemSchema.extend({
1162
1177
  status: z.enum(["unknown", "fresh", "stale", "running", "failed"]),
1163
1178
  baselineVersion: z.string().trim().min(1).max(80).default("amistio-security-baseline-v1"),
1164
1179
  postureScore: z.number().min(0).max(100).optional(),
1165
- postureGrade: z.enum(["A", "B", "C", "D", "F", "Unknown"]).default("Unknown"),
1180
+ postureGrade: securityPostureGradeSchema.default("Unknown"),
1166
1181
  severityCounts: securitySeverityCountsSchema.default(defaultSecuritySeverityCounts),
1167
1182
  categorySummaries: z.array(securityCategorySummarySchema).default([]),
1168
1183
  lastScannedAt: isoDateTimeSchema.optional(),
@@ -4370,6 +4385,8 @@ var appEvaluationStart = "AMISTIO_APP_EVALUATION_START";
4370
4385
  var appEvaluationEnd = "AMISTIO_APP_EVALUATION_END";
4371
4386
  var projectContextRefreshStart = "AMISTIO_PROJECT_CONTEXT_REFRESH_START";
4372
4387
  var projectContextRefreshEnd = "AMISTIO_PROJECT_CONTEXT_REFRESH_END";
4388
+ var validProjectContextEntityTypes = /* @__PURE__ */ new Set(["project", "system", "component", "domain", "tool", "decision", "feature", "risk", "team", "workflow", "unknown"]);
4389
+ var validProjectContextRelationTypes = /* @__PURE__ */ new Set(["uses", "depends_on", "decides", "supersedes", "touches", "blocks", "implements", "mentions"]);
4373
4390
  function createWorkExecutionPrompt(workItem, context) {
4374
4391
  if (workItem.workKind === "brainGeneration") {
4375
4392
  return createBrainGenerationPrompt(workItem);
@@ -4940,7 +4957,13 @@ function parseProjectContextRefreshResult(output) {
4940
4957
  }
4941
4958
  const payload = output.slice(start + projectContextRefreshStart.length, end).trim();
4942
4959
  const parsed = JSON.parse(stripJsonFence(payload));
4943
- return projectContextRefreshResultSchema.parse(parsed);
4960
+ return projectContextRefreshResultSchema.parse(normalizeProjectContextRefreshEnums(parsed));
4961
+ }
4962
+ function projectContextRefreshSubmissionFailureSummary(result) {
4963
+ if (result.refresh.status !== "failed" && result.workItem.status !== "failed") {
4964
+ return void 0;
4965
+ }
4966
+ return result.refresh.error ?? result.workItem.lastStatusMessage ?? "Server rejected the project context refresh result.";
4944
4967
  }
4945
4968
  function createBrainGenerationPrompt(workItem) {
4946
4969
  const wish = workItem.sourceWish ?? workItem.title;
@@ -4998,6 +5021,50 @@ function createBrainGenerationPrompt(workItem) {
4998
5021
  "Do not put Markdown fences around the markers. Do not claim implementation is complete."
4999
5022
  ].join("\n");
5000
5023
  }
5024
+ function normalizeProjectContextRefreshEnums(value) {
5025
+ if (!isObjectRecord(value)) {
5026
+ return value;
5027
+ }
5028
+ const normalized = { ...value };
5029
+ if (Array.isArray(normalized.entities)) {
5030
+ normalized.entities = normalized.entities.map((entity) => {
5031
+ if (!isObjectRecord(entity)) {
5032
+ return entity;
5033
+ }
5034
+ const normalizedEntity = { ...entity };
5035
+ normalizedEntity.entityType = normalizeProjectContextEntityType(entity.entityType);
5036
+ return normalizedEntity;
5037
+ });
5038
+ }
5039
+ if (Array.isArray(normalized.relations)) {
5040
+ normalized.relations = normalized.relations.map((relation) => {
5041
+ if (!isObjectRecord(relation)) {
5042
+ return relation;
5043
+ }
5044
+ const normalizedRelation = { ...relation };
5045
+ normalizedRelation.relationType = normalizeProjectContextRelationType(relation.relationType);
5046
+ return normalizedRelation;
5047
+ });
5048
+ }
5049
+ return normalized;
5050
+ }
5051
+ function normalizeProjectContextEntityType(value) {
5052
+ if (typeof value !== "string") {
5053
+ return "unknown";
5054
+ }
5055
+ const normalized = value.trim().toLowerCase();
5056
+ return validProjectContextEntityTypes.has(normalized) ? normalized : "unknown";
5057
+ }
5058
+ function normalizeProjectContextRelationType(value) {
5059
+ if (typeof value !== "string") {
5060
+ return "mentions";
5061
+ }
5062
+ const normalized = value.trim().toLowerCase();
5063
+ return validProjectContextRelationTypes.has(normalized) ? normalized : "mentions";
5064
+ }
5065
+ function isObjectRecord(value) {
5066
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5067
+ }
5001
5068
  function artifactFormatPreferenceInstructions(preference) {
5002
5069
  if (preference === "html") {
5003
5070
  return "The user chose HTML. Generate .html artifacts under docs/html/<folder>/ and set contentFormat to html.";
@@ -7794,6 +7861,18 @@ ${toolResult.stderr}`);
7794
7861
  ...sessionTelemetry,
7795
7862
  message: `${toolName} returned a project context refresh.`
7796
7863
  });
7864
+ const failureSummary = projectContextRefreshSubmissionFailureSummary(result);
7865
+ if (failureSummary) {
7866
+ await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
7867
+ status: "failed",
7868
+ summary: failureSummary,
7869
+ idempotencyKey: `runner_milestone_project_context_failed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
7870
+ metadata: { tool: toolName, durationMs, sliceCount: refreshResult.slices.length, entityCount: refreshResult.entities.length, verificationSummary: "Server rejected the project context refresh result." }
7871
+ });
7872
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
7873
+ console.error(failureSummary);
7874
+ return { status: "failed", exitCode: 1 };
7875
+ }
7797
7876
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
7798
7877
  status: "completed",
7799
7878
  summary: `${toolName} returned a project context refresh.`,