@enterpriseai/cli 3.19.2 → 3.19.4

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.
Files changed (139) hide show
  1. package/README.md +28 -0
  2. package/dist/commands/chat.d.ts.map +1 -1
  3. package/dist/commands/chat.js +116 -35
  4. package/dist/commands/chat.js.map +1 -1
  5. package/dist/commands/eai-managed-deploy-customer-source.d.ts.map +1 -1
  6. package/dist/commands/eai-managed-deploy-customer-source.js +24 -0
  7. package/dist/commands/eai-managed-deploy-customer-source.js.map +1 -1
  8. package/dist/commands/eai-managed-deploy-github-dispatch.d.ts.map +1 -1
  9. package/dist/commands/eai-managed-deploy-github-dispatch.js +30 -0
  10. package/dist/commands/eai-managed-deploy-github-dispatch.js.map +1 -1
  11. package/dist/commands/eai-managed-source-move.js +2 -2
  12. package/dist/commands/eai-managed-source-move.js.map +1 -1
  13. package/dist/commands/env.js +15 -2
  14. package/dist/commands/env.js.map +1 -1
  15. package/dist/commands/gofer.js +8 -1
  16. package/dist/commands/gofer.js.map +1 -1
  17. package/dist/commands/init.d.ts +4 -0
  18. package/dist/commands/init.d.ts.map +1 -1
  19. package/dist/commands/init.js +187 -8
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/login.d.ts.map +1 -1
  22. package/dist/commands/login.js +6 -2
  23. package/dist/commands/login.js.map +1 -1
  24. package/dist/commands/provision.d.ts.map +1 -1
  25. package/dist/commands/provision.js +27 -13
  26. package/dist/commands/provision.js.map +1 -1
  27. package/dist/commands/publicapi.d.ts.map +1 -1
  28. package/dist/commands/publicapi.js +14 -2
  29. package/dist/commands/publicapi.js.map +1 -1
  30. package/dist/commands/resources.d.ts.map +1 -1
  31. package/dist/commands/resources.js +41 -20
  32. package/dist/commands/resources.js.map +1 -1
  33. package/dist/commands/support.d.ts +18 -0
  34. package/dist/commands/support.d.ts.map +1 -0
  35. package/dist/commands/support.js +110 -0
  36. package/dist/commands/support.js.map +1 -0
  37. package/dist/commands/tenant.js +39 -3
  38. package/dist/commands/tenant.js.map +1 -1
  39. package/dist/commands/types.d.ts +12 -1
  40. package/dist/commands/types.d.ts.map +1 -1
  41. package/dist/commands/types.js +172 -35
  42. package/dist/commands/types.js.map +1 -1
  43. package/dist/commands/verify.d.ts.map +1 -1
  44. package/dist/commands/verify.js +89 -44
  45. package/dist/commands/verify.js.map +1 -1
  46. package/dist/commands/vertical.d.ts.map +1 -1
  47. package/dist/commands/vertical.js +163 -15
  48. package/dist/commands/vertical.js.map +1 -1
  49. package/dist/index.js +54 -7
  50. package/dist/index.js.map +1 -1
  51. package/dist/lib/agent-guide.d.ts.map +1 -1
  52. package/dist/lib/agent-guide.js +25 -4
  53. package/dist/lib/agent-guide.js.map +1 -1
  54. package/dist/lib/api.d.ts +25 -4
  55. package/dist/lib/api.d.ts.map +1 -1
  56. package/dist/lib/api.js +152 -39
  57. package/dist/lib/api.js.map +1 -1
  58. package/dist/lib/cloud-env.d.ts +1 -0
  59. package/dist/lib/cloud-env.d.ts.map +1 -1
  60. package/dist/lib/cloud-env.js +28 -15
  61. package/dist/lib/cloud-env.js.map +1 -1
  62. package/dist/lib/config.d.ts +6 -2
  63. package/dist/lib/config.d.ts.map +1 -1
  64. package/dist/lib/config.js +13 -1
  65. package/dist/lib/config.js.map +1 -1
  66. package/dist/lib/eai-managed-deploy-contract.d.ts +4 -0
  67. package/dist/lib/eai-managed-deploy-contract.d.ts.map +1 -1
  68. package/dist/lib/eai-managed-deploy-contract.js.map +1 -1
  69. package/dist/lib/eai-managed-deploy-operation.d.ts.map +1 -1
  70. package/dist/lib/eai-managed-deploy-operation.js +14 -0
  71. package/dist/lib/eai-managed-deploy-operation.js.map +1 -1
  72. package/dist/lib/eai-managed-deploy-state.d.ts.map +1 -1
  73. package/dist/lib/eai-managed-deploy-state.js +15 -1
  74. package/dist/lib/eai-managed-deploy-state.js.map +1 -1
  75. package/dist/lib/eai-managed-source-client-types.d.ts +3 -0
  76. package/dist/lib/eai-managed-source-client-types.d.ts.map +1 -1
  77. package/dist/lib/error-codes.d.ts.map +1 -1
  78. package/dist/lib/error-codes.js +6 -3
  79. package/dist/lib/error-codes.js.map +1 -1
  80. package/dist/lib/object-type-export-filesystem.d.ts +13 -0
  81. package/dist/lib/object-type-export-filesystem.d.ts.map +1 -0
  82. package/dist/lib/object-type-export-filesystem.js +112 -0
  83. package/dist/lib/object-type-export-filesystem.js.map +1 -0
  84. package/dist/lib/object-type-export.d.ts +13 -0
  85. package/dist/lib/object-type-export.d.ts.map +1 -0
  86. package/dist/lib/object-type-export.js +173 -0
  87. package/dist/lib/object-type-export.js.map +1 -0
  88. package/dist/lib/output.d.ts +1 -0
  89. package/dist/lib/output.d.ts.map +1 -1
  90. package/dist/lib/output.js +24 -1
  91. package/dist/lib/output.js.map +1 -1
  92. package/dist/lib/platform-diagnostics.d.ts +21 -0
  93. package/dist/lib/platform-diagnostics.d.ts.map +1 -0
  94. package/dist/lib/platform-diagnostics.js +50 -0
  95. package/dist/lib/platform-diagnostics.js.map +1 -0
  96. package/dist/lib/profile.d.ts +9 -2
  97. package/dist/lib/profile.d.ts.map +1 -1
  98. package/dist/lib/profile.js +139 -4
  99. package/dist/lib/profile.js.map +1 -1
  100. package/dist/lib/schema-builder.d.ts +2 -0
  101. package/dist/lib/schema-builder.d.ts.map +1 -1
  102. package/dist/lib/schema-builder.js +5 -0
  103. package/dist/lib/schema-builder.js.map +1 -1
  104. package/dist/lib/support-context.d.ts +31 -0
  105. package/dist/lib/support-context.d.ts.map +1 -0
  106. package/dist/lib/support-context.js +193 -0
  107. package/dist/lib/support-context.js.map +1 -0
  108. package/dist/lib/support-redaction.d.ts +10 -0
  109. package/dist/lib/support-redaction.d.ts.map +1 -0
  110. package/dist/lib/support-redaction.js +44 -0
  111. package/dist/lib/support-redaction.js.map +1 -0
  112. package/dist/lib/support-report.d.ts +68 -0
  113. package/dist/lib/support-report.d.ts.map +1 -0
  114. package/dist/lib/support-report.js +165 -0
  115. package/dist/lib/support-report.js.map +1 -0
  116. package/package.json +3 -2
  117. package/resources/deploy/eai-app-template/.github/workflows/eai-app.yml +46 -13
  118. package/resources/deploy/eai-app-template/producer-pin.json +8 -8
  119. package/resources/deploy/eai-app-template/scripts/source-unknown-deployment-evidence.mjs +29 -2
  120. package/resources/deploy/eai-app-template/tests/source-unknown-deployment-evidence.test.mjs +99 -1
  121. package/resources/gofer/agents-skills/eai/SKILL.md +20 -0
  122. package/resources/gofer/claude-commands/eai.md +20 -0
  123. package/resources/gofer/claude-skills/eai/SKILL.md +20 -0
  124. package/resources/gofer/commands/0_gofer_start.md +18 -0
  125. package/resources/gofer/commands/gofer_eai_first_run.md +18 -0
  126. package/resources/gofer/copilot-prompts/eai.prompt.md +20 -0
  127. package/resources/gofer/gemini/commands/gofer/eai.md +20 -0
  128. package/resources/gofer/github-skills/eai/SKILL.md +20 -0
  129. package/resources/gofer/grok-skills/eai/SKILL.md +20 -0
  130. package/resources/gofer/instruction-templates/base/agents-base.md +18 -0
  131. package/resources/gofer/instruction-templates/base/claude-base.md +18 -0
  132. package/resources/gofer/instruction-templates/base/copilot-base.md +18 -0
  133. package/resources/gofer/instruction-templates/gofer/gofer-claude.md +4 -0
  134. package/resources/gofer/node-scripts/generate-commands.mjs +26 -0
  135. package/resources/gofer/references/platform/eai-support.md +19 -0
  136. package/resources/gofer/specify-commands/0_gofer_start.md +18 -0
  137. package/resources/gofer/specify-commands/gofer_eai_first_run.md +18 -0
  138. package/resources/gofer/system-skills/eai/SKILL.md +20 -0
  139. package/resources/linked-sources.json +2 -2
@@ -234,6 +234,7 @@ jobs:
234
234
  --target-tenant-id "$TARGET_TENANT_ID" \
235
235
  --operation-id "$OPERATION_ID" \
236
236
  --nonce "$NONCE" \
237
+ --ref "$GITHUB_REF" \
237
238
  --expected-config-hash "$CONFIG_HASH" \
238
239
  --environment "$DEPLOY_ENVIRONMENT" \
239
240
  --preferred-environment "$PREFERRED_DEPLOY_ENVIRONMENT" \
@@ -450,7 +451,7 @@ jobs:
450
451
  shell: bash
451
452
  run: |
452
453
  if [[ "$LOCAL_E2E_TUNNEL" == true ]]; then
453
- [[ "$SOURCE_MODE" == eai-cli-generated && "$GITHUB_EVENT_NAME" == workflow_dispatch && "$REUSABLE_CALL" != true ]]
454
+ [[ ( "$SOURCE_MODE" == eai-cli-generated || "$SOURCE_MODE" == source-unknown ) && "$GITHUB_EVENT_NAME" == workflow_dispatch && "$REUSABLE_CALL" != true ]]
454
455
  [[ "$DEPLOY_ENVIRONMENT" == dev || "$DEPLOY_ENVIRONMENT" == preview ]]
455
456
  [[ "$EAI_BOUND_PUBLIC_API_URL" =~ ^https://[a-z0-9-]+-8000\.[a-z0-9-]+\.devtunnels\.ms$ ]]
456
457
  else
@@ -795,14 +796,33 @@ jobs:
795
796
  const local = evidence.localE2e;
796
797
  const repositoryId = process.env.GITHUB_REPOSITORY_ID;
797
798
  const expiresAt = process.env.LOCAL_E2E_EXPIRES_AT;
798
- if (!local || Object.keys(local).sort().join(',') !== 'audience,expiresAt,mode,origin,templateCommitSha' ||
799
- local.mode !== 'local-tunnel-v1' || local.origin !== process.env.EAI_BOUND_PUBLIC_API_URL ||
799
+ const sourceUnknownLocal = process.env.SOURCE_MODE === 'source-unknown';
800
+ const expectedKeys = sourceUnknownLocal
801
+ ? 'audience,expiresAt,mode,nonceDigest,origin'
802
+ : 'audience,expiresAt,mode,origin,templateCommitSha';
803
+ if (!local || Object.keys(local).sort().join(',') !== expectedKeys ||
804
+ local.mode !== (sourceUnknownLocal ? 'source-unknown-local-v1' : 'local-tunnel-v1') ||
805
+ local.origin !== process.env.EAI_BOUND_PUBLIC_API_URL ||
800
806
  local.expiresAt !== expiresAt || !/^[1-9][0-9]*$/.test(repositoryId) ||
801
- !/^[a-f0-9]{40}$/.test(local.templateCommitSha) || Date.parse(expiresAt) <= Date.now()) {
807
+ Date.parse(expiresAt) <= Date.now()) {
802
808
  throw new Error('Build evidence local endpoint binding changed.');
803
809
  }
804
- const tuple = ['eai.cli-managed-local-e2e-aud.v1', local.origin, evidence.operationId, repositoryId, local.templateCommitSha, expiresAt];
805
- const audience = 'api://enterprise-ai-publicapi/eai-cli-generated/local-v1/' + createHash('sha256').update(JSON.stringify(tuple)).digest('hex');
810
+ if (sourceUnknownLocal && local.nonceDigest !== 'sha256:' + createHash('sha256').update(evidence.nonce).digest('hex')) {
811
+ throw new Error('Build evidence local nonce binding changed.');
812
+ }
813
+ if (!sourceUnknownLocal && !/^[a-f0-9]{40}$/.test(local.templateCommitSha)) {
814
+ throw new Error('Build evidence local template binding changed.');
815
+ }
816
+ const tuple = sourceUnknownLocal
817
+ ? ['eai.source-unknown-local-e2e-aud.v1', local.origin, process.env.TENANT_ID,
818
+ process.env.TARGET_TENANT_ID || process.env.TENANT_ID, process.env.APP_KEY, evidence.operationId,
819
+ repositoryId, evidence.workflowPath, evidence.ref, evidence.commitSha,
820
+ evidence.configHash, local.nonceDigest, expiresAt]
821
+ : ['eai.cli-managed-local-e2e-aud.v1', local.origin, evidence.operationId, repositoryId, local.templateCommitSha, expiresAt];
822
+ const prefix = sourceUnknownLocal
823
+ ? 'api://enterprise-ai-publicapi/source-unknown/local-v1/'
824
+ : 'api://enterprise-ai-publicapi/eai-cli-generated/local-v1/';
825
+ const audience = prefix + createHash('sha256').update(JSON.stringify(tuple)).digest('hex');
806
826
  if (local.audience !== audience) throw new Error('Build evidence local OIDC audience changed.');
807
827
  } else if (evidence.localE2e !== undefined) {
808
828
  throw new Error('Unexpected local endpoint binding in build evidence.');
@@ -1016,7 +1036,7 @@ jobs:
1016
1036
  set -euo pipefail
1017
1037
  validate_endpoint_and_audience() {
1018
1038
  if [[ "$LOCAL_E2E_TUNNEL" == true ]]; then
1019
- [[ "$SOURCE_MODE" == eai-cli-generated && "$GITHUB_EVENT_NAME" == workflow_dispatch && "$REUSABLE_CALL" != true ]]
1039
+ [[ ( "$SOURCE_MODE" == eai-cli-generated || "$SOURCE_MODE" == source-unknown ) && "$GITHUB_EVENT_NAME" == workflow_dispatch && "$REUSABLE_CALL" != true ]]
1020
1040
  [[ "$DEPLOY_ENVIRONMENT" == dev || "$DEPLOY_ENVIRONMENT" == preview ]]
1021
1041
  [[ "$EAI_BOUND_PUBLIC_API_URL" =~ ^https://[a-z0-9-]+-8000\.[a-z0-9-]+\.devtunnels\.ms$ ]]
1022
1042
  node - <<'NODE'
@@ -1032,14 +1052,27 @@ jobs:
1032
1052
  const local = evidence.localE2e;
1033
1053
  const repoId = process.env.GITHUB_REPOSITORY_ID;
1034
1054
  const expiry = process.env.LOCAL_E2E_EXPIRES_AT;
1035
- if (!local || Object.keys(local).sort().join(',') !== 'audience,expiresAt,mode,origin,templateCommitSha' ||
1036
- local.mode !== 'local-tunnel-v1' || local.origin !== process.env.EAI_BOUND_PUBLIC_API_URL ||
1055
+ const sourceUnknownLocal = process.env.SOURCE_MODE === 'source-unknown';
1056
+ const expectedKeys = sourceUnknownLocal
1057
+ ? 'audience,expiresAt,mode,nonceDigest,origin'
1058
+ : 'audience,expiresAt,mode,origin,templateCommitSha';
1059
+ if (!local || Object.keys(local).sort().join(',') !== expectedKeys ||
1060
+ local.mode !== (sourceUnknownLocal ? 'source-unknown-local-v1' : 'local-tunnel-v1') ||
1061
+ local.origin !== process.env.EAI_BOUND_PUBLIC_API_URL ||
1037
1062
  local.expiresAt !== expiry || !/^[1-9][0-9]*$/.test(repoId) ||
1038
- !/^cli-managed-[a-f0-9]{32}$/.test(evidence.operationId) ||
1039
- !/^[a-f0-9]{40}$/.test(local.templateCommitSha) ||
1040
1063
  !Number.isFinite(Date.parse(expiry)) || Date.parse(expiry) <= Date.now()) process.exit(1);
1041
- const tuple = ['eai.cli-managed-local-e2e-aud.v1', local.origin, evidence.operationId, repoId, local.templateCommitSha, expiry];
1042
- const audience = 'api://enterprise-ai-publicapi/eai-cli-generated/local-v1/' + createHash('sha256').update(JSON.stringify(tuple)).digest('hex');
1064
+ const tuple = sourceUnknownLocal
1065
+ ? ['eai.source-unknown-local-e2e-aud.v1', local.origin, process.env.TENANT_ID,
1066
+ evidence.targetTenantId || process.env.TENANT_ID, process.env.APP_KEY, evidence.operationId, repoId,
1067
+ evidence.workflowPath, evidence.ref, evidence.commitSha, evidence.configHash,
1068
+ local.nonceDigest, expiry]
1069
+ : ['eai.cli-managed-local-e2e-aud.v1', local.origin, evidence.operationId, repoId, local.templateCommitSha, expiry];
1070
+ if (sourceUnknownLocal ? local.nonceDigest !== 'sha256:' + createHash('sha256').update(evidence.nonce).digest('hex')
1071
+ : !/^[a-f0-9]{40}$/.test(local.templateCommitSha)) process.exit(1);
1072
+ const prefix = sourceUnknownLocal
1073
+ ? 'api://enterprise-ai-publicapi/source-unknown/local-v1/'
1074
+ : 'api://enterprise-ai-publicapi/eai-cli-generated/local-v1/';
1075
+ const audience = prefix + createHash('sha256').update(JSON.stringify(tuple)).digest('hex');
1043
1076
  if (local.audience !== audience) process.exit(1);
1044
1077
  process.stdout.write(audience);
1045
1078
  NODE
@@ -2,28 +2,28 @@
2
2
  "schemaVersion": "eai.managed-deploy-producer-pin.v1",
3
3
  "repository": "eai-support/eai-app-template",
4
4
  "candidate": {
5
- "commit": "3a117e2cd12ef2375a80ebdc6031927b2da2be78",
5
+ "commit": "623c4430ba017b8da5b36e84e2fcafd819f79169",
6
6
  "workflow": {
7
7
  "path": ".github/workflows/eai-app.yml",
8
- "sha256": "sha256:e672ee440a434b9d681a73bb00b15c3a2dbb5b0561825cfd7e6abaefd892a4cb"
8
+ "sha256": "sha256:bce4e34fe5c47377e6a679966a37257d944cd00ccf3c15b4ba437f144ed559ed"
9
9
  },
10
10
  "collector": {
11
11
  "path": "scripts/source-unknown-deployment-evidence.mjs",
12
- "sha256": "sha256:ed320c87186579afb3a8a7c9f65b8aea0feadc415a8ec8a0329f741dc106b42f"
12
+ "sha256": "sha256:06e7f33bf1db1d7adbebb75a76d2756e46371318f36d2518c5cfbb58ad4f5e1c"
13
13
  },
14
14
  "evidenceTest": {
15
15
  "path": "tests/source-unknown-deployment-evidence.test.mjs",
16
- "sha256": "sha256:6ca79ea86c03548696d4da6030be04f9dbc6bea60789a68b41b2aec5d115a9d2"
16
+ "sha256": "sha256:fe5f8381d882ac39656b67b30e1e18193cb676a8aba26a6776a88880a3bb5827"
17
17
  }
18
18
  },
19
19
  "releaseGate": {
20
20
  "status": "released",
21
- "tag": "v1.0.10",
22
- "commit": "3a117e2cd12ef2375a80ebdc6031927b2da2be78"
21
+ "tag": "v1.0.13",
22
+ "commit": "623c4430ba017b8da5b36e84e2fcafd819f79169"
23
23
  },
24
24
  "githubProducerIdentity": {
25
25
  "profile": "github-environment-v1",
26
- "templateCommitSha": "3a117e2cd12ef2375a80ebdc6031927b2da2be78",
27
- "workflowSha256": "sha256:e672ee440a434b9d681a73bb00b15c3a2dbb5b0561825cfd7e6abaefd892a4cb"
26
+ "templateCommitSha": "623c4430ba017b8da5b36e84e2fcafd819f79169",
27
+ "workflowSha256": "sha256:bce4e34fe5c47377e6a679966a37257d944cd00ccf3c15b4ba437f144ed559ed"
28
28
  }
29
29
  }
@@ -181,7 +181,7 @@ function localE2eBinding(options, mode, environment, root) {
181
181
  const operationId = option(options, 'operationId');
182
182
  const repositoryId = option(options, 'repositoryId');
183
183
  if (
184
- mode !== 'eai-cli-generated' ||
184
+ !['eai-cli-generated', 'source-unknown'].includes(mode) ||
185
185
  !['preview', 'dev'].includes(environment) ||
186
186
  option(options, 'githubEventName') !== 'workflow_dispatch' ||
187
187
  option(options, 'reusableCall') === 'true' ||
@@ -189,7 +189,6 @@ function localE2eBinding(options, mode, environment, root) {
189
189
  !/^[1-9][0-9]*$/.test(repositoryId) ||
190
190
  !Number.isSafeInteger(Number(repositoryId)) ||
191
191
  String(Number(repositoryId)) !== repositoryId ||
192
- !/^cli-managed-[a-f0-9]{32}$/.test(operationId) ||
193
192
  !/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?\+00:00$/.test(expiresAt) ||
194
193
  !Number.isFinite(Date.parse(expiresAt)) ||
195
194
  Date.parse(expiresAt) <= Date.now() ||
@@ -197,6 +196,34 @@ function localE2eBinding(options, mode, environment, root) {
197
196
  ) {
198
197
  throw new Error('Local deployment requires an exact direct DEV/preview tunnel binding.');
199
198
  }
199
+ if (mode === 'source-unknown') {
200
+ const nonce = option(options, 'nonce');
201
+ const tenantId = option(options, 'tenantId');
202
+ const targetTenant = option(options, 'targetTenantId') || tenantId;
203
+ const appKey = option(options, 'appKey');
204
+ const workflowPath = option(options, 'workflow', '.github/workflows/eai-app.yml');
205
+ const ref = option(options, 'ref', process.env.GITHUB_REF || '');
206
+ const commitSha = option(options, 'commit', process.env.GITHUB_SHA || '');
207
+ const configHash = option(options, 'expectedConfigHash');
208
+ if (!/^source-unknown-[a-f0-9]{16}$/.test(operationId)
209
+ || !tenantId || !targetTenant || !appKey || !nonce
210
+ || workflowPath !== CANONICAL_WORKFLOW_PATH
211
+ || !/^refs\/heads\//.test(ref)
212
+ || !/^[a-f0-9]{40}$/.test(commitSha)
213
+ || !SHA256_DIGEST.test(configHash)) {
214
+ throw new Error('Local source-unknown operation binding is incomplete.');
215
+ }
216
+ const nonceDigest = `sha256:${createHash('sha256').update(nonce).digest('hex')}`;
217
+ const fields = ['eai.source-unknown-local-e2e-aud.v1', origin, tenantId,
218
+ targetTenant, appKey, operationId, repositoryId, workflowPath, ref,
219
+ commitSha, configHash, nonceDigest, expiresAt];
220
+ return { mode: 'source-unknown-local-v1', origin, expiresAt, nonceDigest,
221
+ audience: 'api://enterprise-ai-publicapi/source-unknown/local-v1/'
222
+ + createHash('sha256').update(JSON.stringify(fields)).digest('hex') };
223
+ }
224
+ if (!/^cli-managed-[a-f0-9]{32}$/.test(operationId)) {
225
+ throw new Error('Local CLI operation binding is invalid.');
226
+ }
200
227
  const binding = JSON.parse(readRegularFileNoFollow(root, '.eai/cli-managed-source-operation.json').toString('utf8'));
201
228
  if (
202
229
  binding.schemaVersion !== 'eai.cli_managed_source_operation.v1' ||
@@ -25,12 +25,13 @@ import { tmpdir } from 'node:os';
25
25
  import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
26
26
  import { fileURLToPath } from 'node:url';
27
27
  import test from 'node:test';
28
+ import { runInNewContext } from 'node:vm';
28
29
  import { gunzipSync } from 'node:zlib';
29
30
 
30
31
  const canonicalWorkflowRelativePath = '.github/workflows/eai-app.yml';
31
32
  const fixtureDirectory = 'tests/fixtures/source-unknown';
32
33
  const canonicalDigest =
33
- 'e672ee440a434b9d681a73bb00b15c3a2dbb5b0561825cfd7e6abaefd892a4cb';
34
+ 'bce4e34fe5c47377e6a679966a37257d944cd00ccf3c15b4ba437f144ed559ed';
34
35
  const generatedDigest =
35
36
  '77d4951b3e6852ead73cef8b9e8901e3de774f9aca70f71cc0c932c13903ff32';
36
37
  const digest = (bytes) => createHash('sha256').update(bytes).digest('hex');
@@ -1747,6 +1748,103 @@ test('local CLI dispatch binds direct DEV tunnel, original template and numeric
1747
1748
  }
1748
1749
  });
1749
1750
 
1751
+ test('local customer-owned source uses its own exact direct-operation audience', () => {
1752
+ const root = mkdtempSync(join(tmpdir(), 'eai-source-unknown-local-dispatch-'));
1753
+ try {
1754
+ writeFixtureApp(root);
1755
+ execFileSync('git', ['init', '-q'], { cwd: root });
1756
+ execFileSync('git', ['-c', 'user.name=Fixture', '-c', 'user.email=fixture@example.com',
1757
+ 'commit', '--allow-empty', '-m', 'fixture'], { cwd: root });
1758
+ const commit = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim();
1759
+ const expiry = new Date(Date.now() + 30 * 60 * 1000).toISOString().replace('Z', '+00:00');
1760
+ const args = ['validate-dispatch', '--root', root, '--source-mode', 'source-unknown',
1761
+ '--app-key', 'rates-review', '--tenant-id', 'tenant-parent',
1762
+ '--target-tenant-id', 'hosting-tenant', '--operation-id', `source-unknown-${'a'.repeat(16)}`,
1763
+ '--nonce', 'one-time-source-nonce', '--ref', 'refs/heads/main',
1764
+ '--environment', 'dev', '--expected-config-hash', configHash(root),
1765
+ '--commit', commit, '--workflow-sha', commit,
1766
+ '--public-api-url', 'https://careful-8000.eai.devtunnels.ms',
1767
+ '--local-e2e-tunnel', 'true', '--local-e2e-expires-at', expiry,
1768
+ '--github-event-name', 'workflow_dispatch', '--reusable-call', 'false',
1769
+ '--repository-id', '12345'];
1770
+ runEvidenceScript(args);
1771
+ const withoutTarget = [...args];
1772
+ withoutTarget.splice(withoutTarget.indexOf('--target-tenant-id'), 2);
1773
+ runEvidenceScript(withoutTarget);
1774
+ for (const [key, value] of [
1775
+ ['--public-api-url', 'https://attacker.example'],
1776
+ ['--operation-id', `cli-managed-${'a'.repeat(32)}`],
1777
+ ['--ref', 'refs/pull/1/merge'],
1778
+ ['--environment', 'prod'],
1779
+ ['--local-e2e-expires-at', '2000-01-01T00:00:00+00:00'],
1780
+ ['--repository-id', '0'],
1781
+ ['--reusable-call', 'true'],
1782
+ ]) {
1783
+ const mutated = [...args];
1784
+ mutated[mutated.indexOf(key) + 1] = value;
1785
+ const result = spawnSync(process.execPath, [evidenceScript, ...mutated], { encoding: 'utf8' });
1786
+ assert.equal(result.status, 1, `${key}=${value} must fail`);
1787
+ }
1788
+ } finally {
1789
+ rmSync(root, { recursive: true, force: true });
1790
+ }
1791
+ });
1792
+
1793
+ test('direct local workflow checks the same audience with explicit and omitted target tenants', () => {
1794
+ const workflow = readFileSync(workflowPath, 'utf8');
1795
+ const marker = 'const tuple = sourceUnknownLocal';
1796
+ const first = workflow.indexOf(marker);
1797
+ const second = workflow.indexOf(marker, first + marker.length);
1798
+ assert.ok(first >= 0 && second > first);
1799
+ const snippets = [first, second].map((start) => {
1800
+ const end = workflow.indexOf('if (local.audience !== audience)', start);
1801
+ assert.ok(end > start);
1802
+ return `${workflow.slice(start, end)}\naudience;`;
1803
+ });
1804
+ const origin = 'https://careful-8000.eai.devtunnels.ms';
1805
+ const tenantId = 'tenant-parent';
1806
+ const nonce = 'one-time-source-nonce';
1807
+ const nonceDigest = `sha256:${digest(nonce)}`;
1808
+ const expiry = '2026-10-11T02:00:00+00:00';
1809
+ const evidence = {
1810
+ nonce,
1811
+ operationId: `source-unknown-${'a'.repeat(16)}`,
1812
+ workflowPath: canonicalWorkflowRelativePath,
1813
+ ref: 'refs/heads/main',
1814
+ commitSha: 'b'.repeat(40),
1815
+ configHash: `sha256:${'c'.repeat(64)}`,
1816
+ };
1817
+ const audienceFor = (snippet, targetInput, evidenceTarget) =>
1818
+ runInNewContext(snippet, {
1819
+ createHash,
1820
+ process: { env: {
1821
+ TENANT_ID: tenantId,
1822
+ TARGET_TENANT_ID: targetInput,
1823
+ APP_KEY: 'rates-review',
1824
+ }, exit: () => { throw new Error('Unexpected workflow rejection.'); } },
1825
+ sourceUnknownLocal: true,
1826
+ local: { origin, nonceDigest },
1827
+ evidence: { ...evidence, ...(evidenceTarget ? { targetTenantId: evidenceTarget } : {}) },
1828
+ repositoryId: '12345',
1829
+ repoId: '12345',
1830
+ expiresAt: expiry,
1831
+ expiry,
1832
+ });
1833
+ for (const target of ['', 'hosting-tenant']) {
1834
+ const expectedTarget = target || tenantId;
1835
+ const tuple = ['eai.source-unknown-local-e2e-aud.v1', origin, tenantId,
1836
+ expectedTarget, 'rates-review', evidence.operationId, '12345',
1837
+ evidence.workflowPath, evidence.ref, evidence.commitSha, evidence.configHash,
1838
+ nonceDigest, expiry];
1839
+ const expected = `api://enterprise-ai-publicapi/source-unknown/local-v1/${digest(JSON.stringify(tuple))}`;
1840
+ for (const snippet of snippets) {
1841
+ assert.equal(audienceFor(snippet, target, target), expected);
1842
+ }
1843
+ }
1844
+ assert.notEqual(audienceFor(snippets[1], 'hosting-tenant', 'other-tenant'),
1845
+ audienceFor(snippets[0], 'hosting-tenant', 'other-tenant'));
1846
+ });
1847
+
1750
1848
  test('source commit is resolved before checkout for direct and reusable calls', () => {
1751
1849
  const workflow = readFileSync(workflowPath, 'utf8');
1752
1850
  const stepStart = workflow.indexOf(' - name: Validate workflow invocation');
@@ -185,6 +185,24 @@ Confirm the sign-in method separately: EAI sign-in or client SSO through EAI. Ve
185
185
  7. After any `eai` error, run `eai errors explain <code-or-reason> --format json` when available before guessing remediation.
186
186
  8. Do not write tokens, secrets, private workspace IDs, Entra/CIAM authority tenant IDs, or local `.env` values into artifacts.
187
187
 
188
+ ## Support after an unresolved EAI error
189
+
190
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
191
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
192
+ If the command still fails, prepare a report with
193
+ `eai support --source harness --tool <current-tool> --format json`.
194
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
195
+ This command previews the locally redacted bundle in a noninteractive harness.
196
+ Show the person the bundle summary and ask for explicit consent in the chat.
197
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
198
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
199
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
200
+ The CLI creates the draft; the AI never calls the support API itself.
201
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
202
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
203
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
204
+ If the person asks for help with a silent failure, start with the same report preview.
205
+
188
206
  ## Verified EAI CLI Command Contract
189
207
 
190
208
  Do not invent, guess, or complete EAI CLI commands from memory.
@@ -277,6 +295,8 @@ The EAI Platform supports every step with secure data, identity, multi-tenancy,
277
295
 
278
296
  I’ll walk you through the whole process and ask short questions as we go. Tell me what you want me to do, or ask **“What’s next?”**, **“What else can you help with?”**, or **“Show me the admin screen.”**
279
297
 
298
+ If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
299
+
280
300
  **First question:** What process would you like to improve, or what would you like the application to help people do?
281
301
  ```
282
302
 
@@ -175,6 +175,24 @@ Confirm the sign-in method separately: EAI sign-in or client SSO through EAI. Ve
175
175
  7. After any `eai` error, run `eai errors explain <code-or-reason> --format json` when available before guessing remediation.
176
176
  8. Do not write tokens, secrets, private workspace IDs, Entra/CIAM authority tenant IDs, or local `.env` values into artifacts.
177
177
 
178
+ ## Support after an unresolved EAI error
179
+
180
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
181
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
182
+ If the command still fails, prepare a report with
183
+ `eai support --source harness --tool <current-tool> --format json`.
184
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
185
+ This command previews the locally redacted bundle in a noninteractive harness.
186
+ Show the person the bundle summary and ask for explicit consent in the chat.
187
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
188
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
189
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
190
+ The CLI creates the draft; the AI never calls the support API itself.
191
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
192
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
193
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
194
+ If the person asks for help with a silent failure, start with the same report preview.
195
+
178
196
  ## Verified EAI CLI Command Contract
179
197
 
180
198
  Do not invent, guess, or complete EAI CLI commands from memory.
@@ -267,6 +285,8 @@ The EAI Platform supports every step with secure data, identity, multi-tenancy,
267
285
 
268
286
  I’ll walk you through the whole process and ask short questions as we go. Tell me what you want me to do, or ask **“What’s next?”**, **“What else can you help with?”**, or **“Show me the admin screen.”**
269
287
 
288
+ If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
289
+
270
290
  **First question:** What process would you like to improve, or what would you like the application to help people do?
271
291
  ```
272
292
 
@@ -185,6 +185,24 @@ Confirm the sign-in method separately: EAI sign-in or client SSO through EAI. Ve
185
185
  7. After any `eai` error, run `eai errors explain <code-or-reason> --format json` when available before guessing remediation.
186
186
  8. Do not write tokens, secrets, private workspace IDs, Entra/CIAM authority tenant IDs, or local `.env` values into artifacts.
187
187
 
188
+ ## Support after an unresolved EAI error
189
+
190
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
191
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
192
+ If the command still fails, prepare a report with
193
+ `eai support --source harness --tool <current-tool> --format json`.
194
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
195
+ This command previews the locally redacted bundle in a noninteractive harness.
196
+ Show the person the bundle summary and ask for explicit consent in the chat.
197
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
198
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
199
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
200
+ The CLI creates the draft; the AI never calls the support API itself.
201
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
202
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
203
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
204
+ If the person asks for help with a silent failure, start with the same report preview.
205
+
188
206
  ## Verified EAI CLI Command Contract
189
207
 
190
208
  Do not invent, guess, or complete EAI CLI commands from memory.
@@ -277,6 +295,8 @@ The EAI Platform supports every step with secure data, identity, multi-tenancy,
277
295
 
278
296
  I’ll walk you through the whole process and ask short questions as we go. Tell me what you want me to do, or ask **“What’s next?”**, **“What else can you help with?”**, or **“Show me the admin screen.”**
279
297
 
298
+ If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
299
+
280
300
  **First question:** What process would you like to improve, or what would you like the application to help people do?
281
301
  ```
282
302
 
@@ -333,6 +333,24 @@ in `{FEATURE_DIR}/service-fit-matrix.md` as `unavailable without new platform
333
333
  work`, `operator_required`, or `upgrade_required`. Do not silently replace it
334
334
  with an unrelated non-EAI stack.
335
335
 
336
+ ## Support after an unresolved EAI error
337
+
338
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
339
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
340
+ If the command still fails, prepare a report with
341
+ `eai support --source harness --tool <current-tool> --format json`.
342
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
343
+ This command previews the locally redacted bundle in a noninteractive harness.
344
+ Show the person the bundle summary and ask for explicit consent in the chat.
345
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
346
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
347
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
348
+ The CLI creates the draft; the AI never calls the support API itself.
349
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
350
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
351
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
352
+ If the person asks for help with a silent failure, start with the same report preview.
353
+
336
354
  ### EAI Preflight Checks
337
355
 
338
356
  1. **Classify the build path**
@@ -340,6 +340,24 @@ Specifically note whether the installed CLI advertises the commands needed for:
340
340
  - AI-agent guidance via `eai agent guide`
341
341
  - error recovery guidance via `eai errors explain`
342
342
 
343
+ ## Support after an unresolved EAI error
344
+
345
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
346
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
347
+ If the command still fails, prepare a report with
348
+ `eai support --source harness --tool <current-tool> --format json`.
349
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
350
+ This command previews the locally redacted bundle in a noninteractive harness.
351
+ Show the person the bundle summary and ask for explicit consent in the chat.
352
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
353
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
354
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
355
+ The CLI creates the draft; the AI never calls the support API itself.
356
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
357
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
358
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
359
+ If the person asks for help with a silent failure, start with the same report preview.
360
+
343
361
  ## Step 5: Login, Workspace, And Account Readiness
344
362
 
345
363
  Run:
@@ -187,6 +187,24 @@ Confirm the sign-in method separately: EAI sign-in or client SSO through EAI. Ve
187
187
  7. After any `eai` error, run `eai errors explain <code-or-reason> --format json` when available before guessing remediation.
188
188
  8. Do not write tokens, secrets, private workspace IDs, Entra/CIAM authority tenant IDs, or local `.env` values into artifacts.
189
189
 
190
+ ## Support after an unresolved EAI error
191
+
192
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
193
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
194
+ If the command still fails, prepare a report with
195
+ `eai support --source harness --tool <current-tool> --format json`.
196
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
197
+ This command previews the locally redacted bundle in a noninteractive harness.
198
+ Show the person the bundle summary and ask for explicit consent in the chat.
199
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
200
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
201
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
202
+ The CLI creates the draft; the AI never calls the support API itself.
203
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
204
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
205
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
206
+ If the person asks for help with a silent failure, start with the same report preview.
207
+
190
208
  ## Verified EAI CLI Command Contract
191
209
 
192
210
  Do not invent, guess, or complete EAI CLI commands from memory.
@@ -279,6 +297,8 @@ The EAI Platform supports every step with secure data, identity, multi-tenancy,
279
297
 
280
298
  I’ll walk you through the whole process and ask short questions as we go. Tell me what you want me to do, or ask **“What’s next?”**, **“What else can you help with?”**, or **“Show me the admin screen.”**
281
299
 
300
+ If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
301
+
282
302
  **First question:** What process would you like to improve, or what would you like the application to help people do?
283
303
  ```
284
304
 
@@ -177,6 +177,24 @@ Confirm the sign-in method separately: EAI sign-in or client SSO through EAI. Ve
177
177
  7. After any `eai` error, run `eai errors explain <code-or-reason> --format json` when available before guessing remediation.
178
178
  8. Do not write tokens, secrets, private workspace IDs, Entra/CIAM authority tenant IDs, or local `.env` values into artifacts.
179
179
 
180
+ ## Support after an unresolved EAI error
181
+
182
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
183
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
184
+ If the command still fails, prepare a report with
185
+ `eai support --source harness --tool <current-tool> --format json`.
186
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
187
+ This command previews the locally redacted bundle in a noninteractive harness.
188
+ Show the person the bundle summary and ask for explicit consent in the chat.
189
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
190
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
191
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
192
+ The CLI creates the draft; the AI never calls the support API itself.
193
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
194
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
195
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
196
+ If the person asks for help with a silent failure, start with the same report preview.
197
+
180
198
  ## Verified EAI CLI Command Contract
181
199
 
182
200
  Do not invent, guess, or complete EAI CLI commands from memory.
@@ -269,6 +287,8 @@ The EAI Platform supports every step with secure data, identity, multi-tenancy,
269
287
 
270
288
  I’ll walk you through the whole process and ask short questions as we go. Tell me what you want me to do, or ask **“What’s next?”**, **“What else can you help with?”**, or **“Show me the admin screen.”**
271
289
 
290
+ If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
291
+
272
292
  **First question:** What process would you like to improve, or what would you like the application to help people do?
273
293
  ```
274
294
 
@@ -185,6 +185,24 @@ Confirm the sign-in method separately: EAI sign-in or client SSO through EAI. Ve
185
185
  7. After any `eai` error, run `eai errors explain <code-or-reason> --format json` when available before guessing remediation.
186
186
  8. Do not write tokens, secrets, private workspace IDs, Entra/CIAM authority tenant IDs, or local `.env` values into artifacts.
187
187
 
188
+ ## Support after an unresolved EAI error
189
+
190
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
191
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
192
+ If the command still fails, prepare a report with
193
+ `eai support --source harness --tool <current-tool> --format json`.
194
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
195
+ This command previews the locally redacted bundle in a noninteractive harness.
196
+ Show the person the bundle summary and ask for explicit consent in the chat.
197
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
198
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
199
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
200
+ The CLI creates the draft; the AI never calls the support API itself.
201
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
202
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
203
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
204
+ If the person asks for help with a silent failure, start with the same report preview.
205
+
188
206
  ## Verified EAI CLI Command Contract
189
207
 
190
208
  Do not invent, guess, or complete EAI CLI commands from memory.
@@ -277,6 +295,8 @@ The EAI Platform supports every step with secure data, identity, multi-tenancy,
277
295
 
278
296
  I’ll walk you through the whole process and ask short questions as we go. Tell me what you want me to do, or ask **“What’s next?”**, **“What else can you help with?”**, or **“Show me the admin screen.”**
279
297
 
298
+ If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
299
+
280
300
  **First question:** What process would you like to improve, or what would you like the application to help people do?
281
301
  ```
282
302
 
@@ -185,6 +185,24 @@ Confirm the sign-in method separately: EAI sign-in or client SSO through EAI. Ve
185
185
  7. After any `eai` error, run `eai errors explain <code-or-reason> --format json` when available before guessing remediation.
186
186
  8. Do not write tokens, secrets, private workspace IDs, Entra/CIAM authority tenant IDs, or local `.env` values into artifacts.
187
187
 
188
+ ## Support after an unresolved EAI error
189
+
190
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
191
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
192
+ If the command still fails, prepare a report with
193
+ `eai support --source harness --tool <current-tool> --format json`.
194
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
195
+ This command previews the locally redacted bundle in a noninteractive harness.
196
+ Show the person the bundle summary and ask for explicit consent in the chat.
197
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
198
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
199
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
200
+ The CLI creates the draft; the AI never calls the support API itself.
201
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
202
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
203
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
204
+ If the person asks for help with a silent failure, start with the same report preview.
205
+
188
206
  ## Verified EAI CLI Command Contract
189
207
 
190
208
  Do not invent, guess, or complete EAI CLI commands from memory.
@@ -277,6 +295,8 @@ The EAI Platform supports every step with secure data, identity, multi-tenancy,
277
295
 
278
296
  I’ll walk you through the whole process and ask short questions as we go. Tell me what you want me to do, or ask **“What’s next?”**, **“What else can you help with?”**, or **“Show me the admin screen.”**
279
297
 
298
+ If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
299
+
280
300
  **First question:** What process would you like to improve, or what would you like the application to help people do?
281
301
  ```
282
302
 
@@ -39,3 +39,21 @@ non-app work asks once before skipping EAI tenant/app setup. Artifacts in
39
39
  standards.
40
40
  - **Minimal Impact**: Changes should only touch what's necessary. Avoid
41
41
  introducing bugs.
42
+
43
+ ## Support after an unresolved EAI error
44
+
45
+ After an `eai` command fails, run `eai errors explain <code-or-reason> --format json`.
46
+ Follow its diagnostics, approval requirements, and fixes once. Stop at its retry limit.
47
+ If the command still fails, prepare a report with
48
+ `eai support --source harness --tool <current-tool> --format json`.
49
+ Use `codex`, `claude`, `vscode`, `grok`, or `antigravity` for `<current-tool>`.
50
+ This command previews the locally redacted bundle in a noninteractive harness.
51
+ Show the person the bundle summary and ask for explicit consent in the chat.
52
+ Only after they approve that bundle, repeat the command with `--yes --no-open` and give them the returned link.
53
+ If the bundle changes, show it again and ask for fresh consent. Never assume consent or run `--yes` automatically.
54
+ Keep secrets, tokens, raw logs, and local `.env` values out of the chat.
55
+ The CLI creates the draft; the AI never calls the support API itself.
56
+ Report content stays in the draft body. The Support link fragment carries only its id and token.
57
+ If sign-in is unavailable, give the person the plain Support page link returned by the CLI.
58
+ In the welcome reply, say: If anything fails, say "get help" or type `eai support`; I can prepare a report for you to approve.
59
+ If the person asks for help with a silent failure, start with the same report preview.